有什么理由说明为什么C#中的三元有限?

失败:

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

成功:

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

第一个语句中的错误是:

无法确定条件表达式的类型,因为’int’和’string’之间没有隐式转换。

为什么需要这样,我将这些值分配给object类型的变量。

编辑:上面的例子是微不足道的,是的,但有一些例子,这将是非常有用的:

 int? subscriptionID; // comes in as a parameter EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32) { Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID), } 

使用:

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

问题是条件运算符的返回类型不能无歧义地确定。 也就是说,在int和string之间,没有最佳选择。 编译器将始终使用true表达式的类型,并在必要时隐式转换false表达式。

编辑:在你的第二个例子中:

 int? subscriptionID; // comes in as a parameter EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32) { Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value, } 

PS:
这不称为“三元运营商”。 它一个三元运算符,但它被称为“条件运算符”。

虽然其他答案是正确的 ,但从它们的真实和相关陈述的意义上讲,这里有一些语言设计的微妙点尚未表达。 许多不同的因素有助于条件运算符的当前设计。

首先,希望尽可能多的表达式具有可以仅从表达式的内容确定的明确类型。 由于几个原因,这是可取的。 例如:它使构建IntelliSense引擎变得更加容易。 您输入xM(some-expression.并且IntelliSense需要能够分析某个表达式 ,确定其类型,并在IntelliSense知道xM所指的方法之前生成下拉列表。如果M过载,IntelliSense无法知道xM所指的是什么直到它看到所有的参数,但你还没有输入第一个参数。

其次,我们更喜欢类型信息“从内到外”流动,因为正是我刚刚提到的场景:重载决策。 考虑以下:

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

这应该怎么办? 它应该调用对象超载吗? 它有时会调用字符串重载并有时调用int重载吗? 如果你有另一个重载怎么办,比如说M(IComparable x) – 什么时候选择它?

当类型信息“双向流动”时,事情变得非常复杂。 说“我正在将这个东西分配给一个类型对象的变量,因此编译器应该知道选择对象作为”不洗的类型是可以的; 通常情况下, 我们不知道您要分配的变量的类型,因为这是我们正在试图弄清楚的过程 。 重载决策正是从参数类型中计算出参数类型的过程,参数类型是您为其分配参数的变量。 如果参数的类型取决于它们被分配的类型,那么我们的推理就具有循环性。

类型信息确实为lambda表达式“双向流动”; 实施这项工作有效地带走了我一年中最好的一部分。 我写了很多篇文章,描述了设计和实现编译器时遇到的一些困难,这些编译器可以根据表达式可能被使用的上下文,在类型信息流入复杂表达式的情况下进行分析。 第一部分在这里:

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

您可能会说“好吧,好吧,我明白为什么编译器无法安全地使用我分配给对象的事实,我明白为什么表达式必须具有明确的类型,但为什么不是类型表达式对象,因为int和string都可以转换为object?“ 这让我想到了第三点:

第三,C#的一个微妙但一贯应用的设计原则是“不要通过魔法生成类型”。 当给出一个表达式列表时,我们必须从中确定一个类型, 我们确定的类型总是在列表中的某个位置 。 我们从来没有为新类型制造魔法并为您选择它; 你得到的类型总是你给我们选择的类型。 如果你说要在一组类型中找到最佳类型,我们会在这组类型中找到最佳类型。 在集合{int,string}中,没有最常见的类型,例如“Animal,Turtle,Mammal,Wallaby”。 此设计决策适用于条件运算符,类型推断统一场景,隐式类型数组类型的推断等。

这个设计决策的原因在于它使普通人更容易计算出编译器在必须确定最佳类型的任何给定情况下要做的事情; 如果你知道那种类型就在那里,盯着你看,那将会被选中,那么找出将要发生的事情要容易得多。

它还避免了在发生冲突时我们必须制定关于什么是一组类型的最佳常见类型的复杂规则。 假设你有类型{Foo,Bar},其中两个类都实现了IBlah,并且这两个类都inheritance自Baz。 哪个是最好的常见类型,IBlah,它们都实现了,或Baz,两者都延伸? 我们不想回答这个问题; 我们想完全避免它。

最后,我注意到C#编译器实际上在某些模糊的情况下确定了类型的错误。 我的第一篇文章就在这里:

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

事实上,编译器做得恰到好处并且规范是错误的,这是有争议的; 在我看来,实现设计比规范设计更好。

无论如何,这只是设计三元运算符的这个特定方面的几个原因。 这里还有其他细微之处,例如,CLRvalidation程序如何确定给定的一组分支路径是否保证在所有可能的路径中在堆栈上保留正确的类型。 详细讨论这将使我走得更远。

为什么特征X这种方式通常是一个非常难以回答的问题。 回答实际行为要容易得多。

我的教育猜测为什么。 允许条件运算符简洁明了地使用布尔表达式来选择2个相关值。 它们必须相关,因为它们在一个地方使用。 如果用户改为选择2个不相关的值,那么代码中可能会有一个微妙的拼写错误/错误,编译器最好提醒它们而不是隐式地转换为对象。 这可能是他们没想到的。

“int”是原始类型,而不是对象,而“string”被认为是“原始对象”。 当你执行类似“object o = 1”的操作时,你实际上将“int”装入“Int32”。 这是关于拳击的文章的链接:

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

通常,应该避免拳击,因为性能损失难以追踪。

当您使用三元表达式时,编译器根本不会查看赋值变量来确定最终类型是什么。 要将原始语句分解为编译器正在执行的操作:

声明:对象o =((1 == 2)?1:“test”);

编译:

  1. ‘((1 == 2)?1:“test”)’中的“1”和“test”有哪些类型? 他们匹配吗?
  2. #1的最终类型是否与’object o’的赋值运算符类型匹配?

由于编译器在#1完成之前不评估#2,因此失败。