Je travaille sur une simple bibliothèque de mathématiques à des fins éducatives et j'ai implémenté un actuellement je suis actuellement implémentant un Une matrice utile que je crée un constructeur statique pour est la matrice d'identité. Le code est comme suit: p> Le problème est que cela ne fonctionnera pas car la valeur par défaut de mon Évidemment, une solution est simple et il est de simplement modifier la méthode pour: P> mais cela semble en quelque sorte un gaspillage de Effort comme je suis essentiellement initialise les valeurs de l'ensemble de la matrice deux fois. Je pense en quelque sorte qu'il serait plus élégant de faire la valeur par défaut de J'aimerais souligner que je ne suis pas concerné du tout Performance de code (si cela devait être mon goulot d'étranglement, je serais déjà loin de mes objectifs). Je suis juste curieux de savoir s'il y a un peu de construction (inconnu de moi) qui vous permet d'imposer des valeurs par défaut arbitraires dans un edit forte >: TYPOS P> OK, il semble qu'il n'y a aucun moyen d'imposer des valeurs par défaut arbitraires dans un Peut-on me donner une idée de la raison pour laquelle les structures doivent se comporter de cette façon? Est-ce pour une raison ou a-t-il été mis en œuvre de cette façon parce que personne ne pensait spécifier la possibilité de définir des valeurs par défaut? P> p> struct code> qui représente un Numéro rationnel . Code très basique montrant que les champs principaux de la structure sont les suivants: rationalmatrix code> qui, comme vous l'avez probablement deviné, sera composé de RationalNumber code> Éléments dactylographiés. P> RationalNumber code> n'est pas 0/1 code> mais 0/0 code> qui est un type particulier de valeur ( forme indéterminée ). p> RationalNumber code> égal 0/1 code>. Ce serait facile à faire si RationalNumber était un classe code>, mais je ne peux pas penser à un moyen de le faire quand c'est un struct code>. Est-ce que je manque quelque chose d'évident ou qu'il n'y a pas moyen d'éviter d'avoir 0/0 code> comme la valeur par défaut? P> struct code>. P> code> de l'entrée que je reçois et de mes propres conclusions basées sur ma connaissance C # limitée. P>
4 Réponses :
Il serait agréable de fournir un constructeur par défaut pour la structure: Cependant, cela n'est pas autorisé. Vous pouvez également avoir des initialiseurs d'instance. P> La réponse est simplement que vous devez accepter que le champ interne doit être inalitialisé à zéro, mais cela ne signifie pas que le comportement doit suivre. P> public struct Rational
{
private int _numerator;
private int _denominator;
public Rational(int numerator, int denominator)
{
// Check denominator is positive.
if(denominator < 0){
denominator *= -1;
numerator *= -1;
}
_numerator = numerator;
_denominator = denominator== 0? -1:
denominator;
}
public int Numerator
{
get { return _numerator; }
}
public int Denominator
{
get { return
_denominator == 0?1:
_denominator == -1?0:
_denominator; }
}
}
Depuis quand est-ce permis?
-1 qui ne fonctionnera pas. Création d'une matrice de RationalNumber code> n'appellera pas ce constructeur et, comme tel, il n'est pas autorisé: "" Structs ne peut pas contenir des constructeurs sans paramètres explicites " i>
Ben, votre deuxième suggestion ne fonctionnera pas non plus: "ne peut pas avoir d'initialistes de champ d'instance dans les structures" i>. Il y a une raison pour laquelle il n'y a pas d'autre réponse à vos côtés: ce n'est pas possible.
C'est ce que je vais faire, mais avec la suggestion d'Arvo d'utiliser un champ booléen initialisé. 0/0 est une valeur valide pour un numéro rationnel même si son indéterminé, donc je ne peux pas les traiter tous comme s'il était égal à 0 / N.
@Inbetween, je me suis développé pour permettre le 0/0 à être représenté et ajouter une manipulation correcte pour les dénominateurs négatifs.
Si vous n'avez pas à distinguer les valeurs indéterminées 0/0 et d'autres valeurs 0 / N, vous pouvez traiter tout votre 0 / N comme zéro. C'est-à-dire que tous les zéros sont égaux qui a du sens (0/2 égal à 0/1), ainsi que toutes les divisions à zéro sont égales, donc 1/0 == 2/0.
Merci pour l'idée. Malheureusement, 0/0 est radicalement différent de 0 / N et je dois considérer cela de cette façon dans ma bibliothèque, donc ce n'est pas une option. Je pense que la meilleure approche en ce moment est de suivre les conseils de Arvo et de créer un domaine booléen qui marque la structure comme étant explicitement initialisée ou non.
Oui, si vous devez séparer ces deux états, vous avez besoin d'un bit d'informations supplémentaires. Aussi: Je prendrais un pic faufilé à la source de celui-ci msdn.microsoft.com/en-us/library/...
Merci! Je ne savais pas que c'était déjà implémenté dans le .NET Framework. Je vais certainement vérifier. Quoi qu'il en soit, l'objectif éducatif est également pour moi. Je vais pratiquer un code C # et rappelez-vous quelques mathématiques "de base" en même temps;)
Il est bon que possible de concevoir des structures de manière à ce que toute combinaison de valeurs de champ ait une sémantique définie. Si cela n'est pas fait, la structure ne sera souvent aucun moyen de prévenir la construction d'instances mal formées par un code mal formé et de ces instances de causer un comportement inapproprié dans le code qui est correctement fileté. Par exemple, si un emplacement de stockage de type rationnel avait des valeurs de numérateur et de dénominateur reconnues comme étant définitivement coprime et que ledit emplacement a été copié dans un filetage tandis que sa valeur a été modifiée dans un autre fil, le fil qui a fait la copie pourrait recevoir une instance. où le numérateur et le dénominateur n'étaient pas coprime, mais le drapeau a dit qu'ils étaient. Autre code qui a reçu cette instance pourrait échouer de manière étrange et bizarre à la suite de l'invariant cassé; Une telle défaillance peut se produire quelque part très éloignée du code non threadsafe qui a créé l'instance cassée.
Cette situation peut être corrigée en utilisant un objet de classe immuable pour contenir le nombre rationnel et avoir un type de valeur rationnelle qui enveloppe une référence privée à un tel objet. Le type d'emballage utiliserait une instance par défaut lorsque sa référence privée est NULL ou l'instance enveloppée lorsqu'elle n'est pas. Cette approche pourrait offrir des améliorations d'efficacité potentielles si la référence privée était un type abstrait et plusieurs types dérivés ont satisfait à différents critères différents. Par exemple, on pourrait avoir un La première instruction définirait le champ privé de R1 pour se reporter à un RationalsMallInteger Code> dont le seul champ était un int32 code> et un rationallonginteger code> dont le seul champ était un int64 < / Code> (la propriété code> dénominateur code> de ces types de ces types renvoie toujours 1). On pourrait avoir des types où le dénominateur était non nul mais a été validé comme étant en coprime avec le numérateur ou les types où ce n'était pas; Ce dernier type de type pourrait contenir une référence initialement-null à une instance où le numérateur et le dénominateur étaient garantis en coprime. Un tel comportement pourrait améliorer l'efficacité des cas comme: p> RationalNumber.int32By32NonRèse code> instance. La seconde définirait le champ privé de R2 pour pointer vers cette même instance. La troisième déclaration générerait une nouvelle instance int32by32reduited Code> et stocker une référence à celle de l'ancien int32by32nonreduited code> instance, ainsi que dans le champ privé de R3. Le quatrième va chercher la référence susmentionnée de l'ancien int32by32réduit code> et le stocke dans le champ privé de R4. Notez qu'une seule opération de réduction serait nécessaire. En revanche, si RationalNumber code> était une structure qui détenait ses valeurs en interne, la quatrième déclaration n'aurait aucun moyen de réutiliser le résultat de la réduction effectuée par la troisième. P> P>
Vous ne pouvez pas avoir un constructeur sans paramètre qui attribue une valeur par défaut. La raison technique est que votre Le plus proche que vous puissiez obtenir est probablement la solution de David Heurman: p> alors vous pouvez simplement référence Vous pouvez faire des choses comme appelez un constructeur avec des paramètres ou créez un En fait, le nouveau nouveau produit rationnel [100] [100] [100] code> Convention n'est pas simplement un sténographie codant, mais il court également f aster que d'appeler le constructeur 10 000 fois. Cela fait partie de la raison pour laquelle La boucle à travers chaque élément de la matrice offre un avantage de clarté, mais en utilisant la solution "bizarre" moins une solution réduit non seulement la quantité de code que vous doivent courir, mais fournit des améliorations de la performance. Donc, vous pourriez prendre cela comme un argument fort en sa faveur. P> p> struct code> est une sous-classe de système.valueType code> et system.valueType () code> est protégé code >, alors ne peut pas être remplacé. dénominateur code> Votre code normalement et le format de stockage spécial sera transparent - vous ne serez capable de dire que en examinant la déclaration de champ ou en examinant le comportement du constructeur par défaut. P> rationnalatefactory code> classe pour produire des zéros pour vous - mais aucun de ceux-ci ne se contenterait de votre émission de boucle via chaque élément de la matrice au lieu de la diagonale, car vous ne pouvez pas spécifier le constructeur qu'un initialiseur de tableau utilisera. p> system.valueType () code> a été effectué protégé code> en premier lieu. Voir: Pourquoi ne puis-je pas définir un constructeur par défaut pour une structure dans .NET? p>
Jolie dégoûtante, mais vous pouvez changer
dénominateur code> versdénominatorminusone code>! Donc, lorsquedénominatorminusone == 0 code> qui signifierait quedénominateur == 1 code>. J'ai du mal à recommander cela comme étant une bonne idée!@DavidHeffernan Peut-être pas
dénominatorminusone code> comme cela implique une plage différente, mais peut-êtredénominatorxorone code>? Même si ce n'est pas une bonne idée, ce n'est peut-être que l'idée i> qui fonctionne!Vous pouvez ajouter quelque chose comme «privé bool isinitialisé»; et utilisez cette propriété de manière créative dans vos méthodes.
@David Heffernan: :) Cela semble horrible oui. Merci pour la suggestion mais j'aimerais éviter cela à tout prix.
@Arvo: Yup qui semble être la meilleure approche. Je vais modifier ma question et élargir sa portée et attendre que certains C # Guru me donnent une idée de la raison pour laquelle les structures ont ce comportement.
C'est juste que les évaluations fonctionnent. Voulez-vous le préférer rempli de bits aléatoires?
@leppie: Non, où ai-je jamais impliqué je voulais ça? Je préférerais que vous puissiez écrire votre propre constructeur sans paramètre et faire vérifier le compilateur que tous les champs sont définitivement attribués comme dans tout autre constructeur de structure. Pourquoi est-ce que vous n'êtes pas autorisé?
@Inbetween: parce que cela serait cher. Imaginez appeler
Nouveau rationnel [1000000] code>, il faudrait très longtemps par rapport à l'appelantcalloc code>.@Leppie: Je comprends ça, mais pourquoi je ne peux pas y faire mon choix? Pourquoi le compilateur ou le CLR doivent-ils décider de ce que je veux? Si je ne veux pas payer le prix, je ne définirai pas un constructeur sans paramètre. Si je le fais, c'est parce que je veux ou peut gérer le coût. Dans mon exemple, je dois essentiellement passer à travers l'ensemble de la matrice définissant toutes les valeurs de sorte que le surprise serait-il si je pouvais définir la valeur par défaut pour commencer?
@Inbetween: ce comportement est par conception. Comme dit déjà dit, la struct est de type de valeur - cela signifie que chaque affectation, paramètre qui passe, etc. en fait une nouvelle copie. Comment imaginez-vous copier votre structure, s'il avait un constructeur sans paramètre? Copier des bits et appliquer un constructeur? Créer une structure vide, appliquer un constructeur et copier des bits? Soit le scénario n'a pas de sens, a des effets secondaires méchant, modifie les contenus de la structure ou quelque chose de similaire indésirable. Si vous avez vraiment besoin de constructeurs, utilisez des cours :)
En outre, mineur Nitpick: Vous voulez probablement
zéro code> pour être unconst code> passtatique code>. Je doute que la définition de zéro change probablement dans la durée de vie de votre programme, ni différent de l'exécution à l'exécution.