C# 中的三元数受到限制有什么充分的理由吗?

发布于 2024-08-10 11:41:27 字数 580 浏览 7 评论 0原文

失败:

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),
}

Fails:

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

Succeeds:

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

The error in the first statement is:

Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and 'string'.

Why does there need to be though, I'm assigning those values to a variable of type object.

Edit: The example above is trivial, yes, but there are examples where this would be quite helpful:

int? subscriptionID; // comes in as a parameter

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我偏爱纯白色 2024-08-17 11:41:27

使用:

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:
这不称为“三元运算符”。它一个三元运算符,但它被称为“条件运算符”。

use:

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

The issue is that the return type of the conditional operator cannot be un-ambiguously determined. That is to say, between int and string, there is no best choice. The compiler will always use the type of the true expression, and implicitly cast the false expression if necessary.

Edit:
In you second example:

int? subscriptionID; // comes in as a parameter

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

PS:
That is not called the 'ternary operator.' It is a ternary operator, but it is called the 'conditional operator.'

风和你 2024-08-17 11:41:27

尽管其他答案是正确,但就它们做出真实且相关的陈述而言,这里有一些语言设计的微妙点尚未表达。许多不同的因素影响了条件运算符的当前设计。

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

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

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

它应该做什么?它应该有时调用字符串重载,有时调用 int 重载吗? code>M(IComparable x) -- 什么时候选择它?

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

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

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

您可能会说“好吧,我明白为什么编译器不能安全地使用我分配给对象的事实,并且我明白为什么表达式必须具有明确的类型,但为什么不是表达式对象的类型,因为 int 和字符串可以转换为对象吗?”这引出了我的第三点:

第三,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 验证程序如何确定给定的一组分支路径是否保证在所有可能路径中的堆栈上留下正确的类型。详细讨论这个问题会让我离题很远。

Though the other answers are correct, in the sense that they make true and relevant statements, there are some subtle points of language design here that haven't been expressed yet. Many different factors contribute to the current design of the conditional operator.

First, it is desirable for as many expressions as possible to have an unambiguous type that can be determined solely from the contents of the expression. This is desirable for several reasons. For example: it makes building an IntelliSense engine much easier. You type x.M(some-expression. and IntelliSense needs to be able to analyze some-expression, determine its type, and produce a dropdown BEFORE IntelliSense knows what method x.M refers to. IntelliSense cannot know what x.M refers to for sure if M is overloaded until it sees all the arguments, but you haven't typed in even the first argument yet.

Second, we prefer type information to flow "from inside to outside", because of precisely the scenario I just mentioned: overload resolution. Consider the following:

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

What should this do? Should it call the object overload? Should it sometimes call the string overload and sometimes call the int overload? What if you had another overload, say M(IComparable x) -- when do you pick it?

Things get very complicated when type information "flows both ways". Saying "I'm assigning this thing to a variable of type object, therefore the compiler should know that it's OK to choose object as the type" doesn't wash; it's often the case that we don't know the type of the variable you're assigning to because that's what we're in the process of attempting to figure out. Overload resolution is exactly the process of working out the types of the parameters, which are the variables to which you are assigning the arguments, from the types of the arguments. If the types of the arguments depend on the types to which they're being assigned, then we have a circularity in our reasoning.

Type information does "flow both ways" for lambda expressions; implementing that efficiently took me the better part of a year. I've written a long series of articles describing some of the difficulties in designing and implementing a compiler that can do analysis where type information flows into complex expressions based on the context in which the expression is possibly being used; part one is here:

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

You might say "well, OK, I see why the fact that I'm assigning to object cannot be safely used by the compiler, and I see why it's necessary for the expression to have an unambiguous type, but why isn't the type of the expression object, since both int and string are convertible to object?" This brings me to my third point:

Third, one of the subtle but consistently-applied design principles of C# is "don't produce types by magic". When given a list of expressions from which we must determine a type, the type we determine is always in the list somewhere. We never magic up a new type and choose it for you; the type you get is always one that you gave us to choose from. If you say to find the best type in a set of types, we find the best type IN that set of types. In the set {int, string}, there is no best common type, the way there is in, say, "Animal, Turtle, Mammal, Wallaby". This design decision applies to the conditional operator, to type inference unification scenarios, to inference of implicitly typed array types, and so on.

The reason for this design decision is that it makes it easier for ordinary humans to work out what the compiler is going to do in any given situation where a best type must be determined; if you know that a type that is right there, staring you in the face, is going to be chosen then it is a lot easier to work out what is going to happen.

It also avoids us having to work out a lot of complex rules about what's the best common type of a set of types when there are conflicts. Suppose you have types {Foo, Bar}, where both classes implement IBlah, and both classes inherit from Baz. Which is the best common type, IBlah, that both implement, or Baz, that both extend? We don't want to have to answer this question; we want to avoid it entirely.

Finally, I note that the C# compiler actually gets the determination of the types subtly wrong in some obscure cases. My first article about that is here:

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

It's arguable that in fact the compiler does it right and the spec is wrong; the implementation design is in my opinion better than the spec'd design.

Anyway, that's just a few reasons for the design of this particular aspect of the ternary operator. There are other subtleties here, for instance, how the CLR verifier determines whether a given set of branching paths are guaranteed to leave the correct type on the stack in all possible paths. Discussing that in detail would take me rather far afield.

灯角 2024-08-17 11:41:27

为什么功能 X 会这样通常是一个很难回答的问题。回答实际行为要容易得多。

我有根据的猜测为什么。条件运算符可以简洁地使用布尔表达式在 2 个相关值之间进行选择。它们必须是相关的,因为它们是在单个位置使用的。如果用户选择了 2 个不相关的值,则代码中可能存在细微的拼写错误/错误,编译器最好警告他们这一点,而不是隐式转换为对象。这或许是他们没有想到的。

Why is feature X this way is often a very hard question to answer. It's much easier to answer the actual behavior.

My educated guess as to why. The conditional operator is allowed to succinctly and tersely use a boolean expression to pick between 2 related values. They must be related because they are being used in a single location. If the user instead picks 2 unrelated values perhaps the had a subtle typo / bug in there code and the compiler is better off alerting them to this rather than implicitly casting to object. Which may be something they did not expect.

好久不见√ 2024-08-17 11:41:27

“int”是原始类型,而不是对象,而“string”则被认为更多是“原始对象”。当您执行“object o = 1”之类的操作时,实际上是将“int”装箱为“Int32”。以下是有关拳击的文章的链接:

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

一般来说,应避免装箱,因为性能损失难以追踪。

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

语句:
对象 o = ((1==2) ? 1 : "测试");

编译器:

  1. '((1==2) ? 1 : "test")' 中的“1”和“test”是什么类型?他们匹配吗?
  2. #1 中的最终类型是否与“object o”的赋值运算符类型匹配?

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

"int" is a primitive type, not an object while "string" is considered more of a "primitive object". When you do something like "object o = 1", you're actually boxing the "int" to an "Int32". Here's a link to an article about boxing:

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

Generally, boxing should be avoided due to performance loses that are hard to trace.

When you use a ternary expression, the compiler does not look at the assignment variable at all to determine what the final type is. To break down your original statement into what the compiler is doing:

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

Compiler:

  1. What are the types of "1" and "test" in '((1==2) ? 1 : "test")'? Do they match?
  2. Does the final type from #1 match the assignment operator type for 'object o'?

Since the compiler doesn't evaluate #2 until #1 is done, it fails.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文