失败:

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

成功:

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

第一条语句的错误是:

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

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

编辑: 是的,上面的例子很简单,但是有一些例子会非常有帮助:

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之间,没有最佳选择。编译器将始终使用真实表达式的类型,并在必要时隐式地施放虚假表达式。

编辑:在您的第二个示例中:

int? subscriptionID; // comes in as a parameter

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

PS:
这不称为“三元操作员”。它 三元运算符,但称为“条件操作员”。

其他提示

虽然其他答案都是 正确的, ,从他们做出真实且相关的陈述的意义上来说,这里有一些语言设计的微妙点尚未表达出来。许多不同的因素影响了条件运算符的当前设计。

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

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

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

这个应该怎么办呢?它应该调用对象重载吗?它是否应该有时调用 string 重载,有时调用 int 重载?如果你再次超载怎么办? M(IComparable x) ——什么时候选?

当类型信息“双向流动”时,事情会变得非常复杂。说“我正在将这个东西分配给对象类型的变量,因此编译器应该知道选择对象作为类型是可以的”并不能说明问题;通常的情况是 我们不知道您要分配的变量的类型,因为这就是我们正在尝试找出的过程. 。重载解析正是根据参数的类型计算出参数类型的过程,参数是要为其分配参数的变量。如果参数的类型取决于它们被分配的类型,那么我们的推理就会出现循环。

对于 lambda 表达式,类型信息确实“双向流动”;有效地实现这一点花了我一年的大部分时间。我写了一系列文章,描述了设计和实现编译器时遇到的一些困难,该编译器可以根据可能使用表达式的上下文来分析类型信息流入复杂表达式的情况;第一部分在这里:

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

您可能会说:“好吧,我知道为什么要分配给对象的事实不能由编译器安全地使用,我明白了为什么表达式具有明确的类型,但是为什么不这样的类型表达式对象,因为int和string均可转换为对象?”这将我带到了我的第三点:

第三,C# 微妙但一贯应用的设计原则之一是“不要用魔法产生类型”。当给定一个表达式列表时,我们必须从中确定类型, 我们确定的类型总是在列表中的某个地方. 。我们从来不会创造出一种新类型并为您选择;您得到的类型始终是您给我们选择的类型。如果你说要在一组类型中找到最佳类型,我们会在该组类型中找到最佳类型。在集合 {int, string} 中,不存在最佳常见类型,例如“动物、海龟、哺乳动物、小袋鼠”。此设计决策适用于条件运算符、类型推断统一场景、隐式类型数组类型的推断等等。

这种设计决策的原因是,它使普通人更容易弄清楚编译器在必须确定最佳类型的任何给定情况下将要做什么;如果你知道某个类型就在那里,盯着你的脸,将会被选择,那么就更容易弄清楚将会发生什么。

它还避免了我们在存在冲突时必须制定大量复杂的规则来确定一组类型中最好的常见类型。假设您有类型 {Foo, Bar},其中两个类都实现了 IBlah,并且两个类都继承自 Baz。哪个是最好的通用类型,IBlah(两者都实现)或 Baz(两者都扩展)?我们不想回答这个问题;我们想完全避免它。

最后,我注意到,在一些模糊的情况下,C# 编译器实际上对类型的确定存在微妙的错误。我的第一篇关于此的文章在这里:

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

事实上,编译器做得正确而规范是错误的,这是有争议的;我认为实现设计比规范设计更好。

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

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

我对为什么的教育猜测。允许条件运算符简洁,简洁地使用布尔表达式来选择2个相关值。它们必须与之相关,因为它们被用于单个位置。如果用户选择2个无关的值,则可能在那里的代码中有一个微妙的错别字 /错误,并且编译器最好提醒他们对此,而不是隐式施放对象。这可能是他们没想到的。

“ int”是一种原始类型,而不是“字符串”时的对象,而不是“原始对象”。当您执行“对象o = 1”之类的事情时,实际上您将“ int”拳击为“ int32”。这是有关拳击文章的链接:

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

通常,由于性能损失,应避免拳击。

当您使用三元表达式时,编译器根本不会查看分配变量以确定最终类型是什么。将您的原始声明分解为编译器的所作所为:

语句:对象o =((1 == 2)?1:“ test”);

编译器:

  1. '((1 == 2)?1:“ test”)中的“ 1”和“测试”类型是什么?他们匹配了吗?
  2. #1的最终类型是否匹配“对象O”的分配运算符类型?

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top