Question

Ne parvient pas:

object o = ((1==2) ? 1 : "test");

Réussit:

object o;
if (1 == 2)
{
    o = 1;
}
else
{
    o = "test";
}

L'erreur dans la première déclaration est:

  

Type d'expression conditionnelle ne peut être déterminée parce qu'il n'y a pas de conversion implicite entre les deux. « Int » et « string »

Pourquoi il faut être bien, j'attribuer ces valeurs à une variable d'objet de type.

Modifier L'exemple ci-dessus est trivial, oui, mais il y a des exemples où cela serait très utile:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID),
}
Était-ce utile?

La solution

utilisation:

object o = ((1==2) ? (object)1 : "test");

Le problème est que le type de retour de l'opérateur conditionnel ne peut pas être non déterminé de façon ambiguë. C'est-à-dire entre int et string, il n'y a pas meilleur choix. Le compilateur utilise toujours le type de la véritable expression, et implicitement le faux jeté expression si nécessaire.

Edit: Vous deuxième exemple:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value,
}

PS: Ce n'est pas appelé « opérateur ternaire ». Il est un opérateur ternaire, mais il est appelé 'opérateur conditionnel'.

Autres conseils

Bien que les autres réponses sont correcte , dans le sens où ils font des déclarations vraies et pertinentes, il y a quelques points subtils de la conception linguistique ici qui ne sont pas encore exprimées. De nombreux facteurs contribuent à la conception actuelle de l'opérateur conditionnel.

Tout d'abord, il est souhaitable que le plus grand nombre d'expressions que possible d'avoir un type non ambigu qui peut être déterminé uniquement à partir du contenu de l'expression. Ceci est souhaitable pour plusieurs raisons. Par exemple: il rend la construction d'un moteur IntelliSense beaucoup plus facile. Vous tapez x.M(some-expression. et IntelliSense doit être en mesure d'analyser une expression , déterminer son type, et produire un menu déroulant AVANT IntelliSense sait quelle méthode x.M fait référence. IntelliSense ne peut pas savoir ce que x.M fait référence à certitude si M est surchargé jusqu'à ce qu'il voit tous les arguments, mais vous n'avez pas tapé encore même le premier argument.

En second lieu, nous préférons des informations de type à flux « de l'intérieur vers l'extérieur », en raison du scénario que je viens de mentionner précisément: la résolution de surcharge. Considérez ce qui suit:

void M(object x) {}
void M(int x) {}
void M(string x) {}
...
M(b ? 1 : "hello");

Que devrait-il faire? Faut-il appeler la surcharge d'objet? Faut-il appeler parfois la surcharge de chaîne et parfois appeler la surcharge int? Que faire si vous aviez une autre surcharge, dire M(IComparable x) - quand choisir-vous?

Les choses deviennent très compliquées lorsque les informations de type « flux dans les deux sens ». Dire «J'assignant cette chose à une variable d'objet de type, donc le compilateur doit savoir qu'il est correct de choisir le type objet » ne se lave pas; il est souvent le cas que nous ne connaissons pas le type de la variable que vous affectez parce que est ce que nous sommes en train d'essayer de comprendre . Résolution de surcharge est exactement le processus de travail sur les types des paramètres, qui sont les variables auxquelles vous attribuez les arguments, des types des arguments. Si les types des arguments dépendent des types auxquels ils sont affectés étant, nous avons une circularité dans notre raisonnement.

Les informations de type ne "flux dans les deux sens" pour les expressions lambda; la mise en œuvre efficace qui m'a pris la meilleure partie d'une année. J'ai écrit une longue série d'articles décrivant quelques-unes des difficultés dans la conception et la mise en œuvre d'un compilateur qui peut faire une analyse où l'information de type flux dans des expressions complexes en fonction du contexte dans lequel l'expression est peut-être utilisé; La première partie est ici:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx

Vous pourriez dire: « Eh bien, OK, je vois pourquoi le fait que je l'affectation à l'objet ne peut pas être utilisé en toute sécurité par le compilateur, et je vois pourquoi il est nécessaire pour l'expression d'avoir un type sans ambiguïté, mais pourquoi n » t le type de l'objet d'expression, puisque les deux int et la chaîne sont convertibles en objet? » Cela me amène à mon troisième point:

En troisième lieu, l'un des principes de conception subtile mais constante appliquée de C # est « ne produisent pas de types par magie ». Lorsqu'on leur donne une liste d'expressions dont nous devons déterminer un type, le type nous déterminons est toujours dans la liste quelque part . Nous ne avons jamais la magie d'un nouveau type et choisir pour vous; le type que vous obtenez est toujours celui que vous nous avez donné à choisir. Si vous dites à trouver le meilleur type dans un ensemble de types, nous trouvons le meilleur type de cet ensemble de types. Dans l'ensemble {int, string}, il n'y a pas meilleur type commun, la façon dont il est, par exemple, « animal, tortue, mammifères, Wallaby ». Cette décision de conception applique à l'opérateur conditionnel, pour saisir des scénarios d'unification inférence, à l'inférence des types de tableaux implicitement typés, et ainsi de suite.

La raison de cette décision de conception est qu'il rend plus facile pour l'homme ordinaire de travailler ce que le Compiler va faire dans une situation donnée où un meilleur type doit être déterminé; si vous savez qu'un type qui est là, vous regardant dans le visage, va plus facile à choisir alors il est beaucoup travailler ce qui va se passer.

Il permet également d'éviter que nous ayons à travailler un grand nombre de règles complexes sur ce qui est le meilleur type commun d'un ensemble de types quand il y a des conflits. Supposons que vous ayez les types {Foo, Bar}, où les deux classes implémentent IBlah, et les deux classes héritez de Baz. Quel est le meilleur type commun, IBlah, que les deux mettre en œuvre, ou Baz, que les deux étendre? Nous ne voulons pas avoir à répondre à cette question; nous voulons éviter complètement.

Enfin, je note que le compilateur C # obtient effectivement la détermination des types subtilement mal dans certains cas obscurs. Mon premier article à ce sujet ici:

http: // blogs.msdn.com/ericlippert/archive/2006/05/24/type-inference-woes-part-one.aspx

Il est possible de soutenir que, en fait, le compilateur le fait droit et la spécification est faux; la conception de la mise en œuvre est à mon avis mieux que la conception spec'd.

Quoi qu'il en soit, c'est juste quelques raisons pour la conception de cet aspect particulier de l'opérateur ternaire. Il existe d'autres subtilités ici, par exemple, comment le vérificateur CLR détermine si un ensemble de chemins de branchement sont garantis de laisser le bon type sur la pile dans tous les chemins possibles. Discuter en détail que me prendre un peu loin.

Pourquoi est fonction X de cette façon est souvent une question très difficile à répondre. Il est beaucoup plus facile de répondre au comportement réel.

Ma supposition pourquoi. L'opérateur conditionnel est autorisé à utiliser de façon succincte et laconiquement une expression booléenne pour choisir entre 2 valeurs liées. Ils doivent être liés parce qu'ils sont utilisés dans un seul endroit. Si l'utilisateur choisit 2 au lieu des valeurs sans rapport avec peut-être la faute de frappe avait une subtile / bug dans le code et il est le compilateur mieux les avertir de cela plutôt que de jeter implicitement à l'objet. Ce qui est peut-être quelque chose qu'ils ne s'attendaient pas.

« int » est un type primitif, pas un objet alors que « string » est considéré comme plus d'un « objet primitif ». Quand vous faites quelque chose comme « objet o = 1 », vous êtes en train de boxe le « int » à un « Int32 ». Voici un lien vers un article sur la boxe:

http://msdn.microsoft.com/en-us/magazine /cc301569.aspx

En général, il faut éviter la boxe en raison de la performance perd qui sont difficiles à tracer.

Lorsque vous utilisez une expression ternaire, le compilateur ne regarde pas la variable d'affectation du tout pour déterminer ce que le dernier type est. Pour briser votre déclaration d'origine dans ce que le compilateur fait:

Déclaration: objet o = ((1 == 2) 1: "test");

Compiler:

  1. Quels sont les types de "1" et "test" dans '((1 == 2) 1: "test")? Correspondent-ils?
  2. Est-ce que le dernier type de jeu n ° 1 du type d'opérateur d'affectation pour 'objet o'?

Étant donné que le compilateur n'évalue pas # 2 jusqu'à ce que # 1 est fait, il échoue.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top