为什么我必须在三元表达式中类型转换 int ?

发布于 2024-09-07 20:02:46 字数 645 浏览 8 评论 0原文

可能的重复:
条件运算符不能隐式转换?

我遇到了一种特殊的情况,想知道为什么我必须这么做。我正在使用 .NET 3.5。

这有效:

short foo;

if (isValid)
    foo = -1;
else
    foo = getFoo();

这不起作用:

short foo;
foo = isValid ? -1 : getFoo();

我必须进行类型转换 -1:

short foo;
foo = isValid ? (short)-1 : getFoo();

三元表达式有何不同?它认为 -1 是一个需要转换为短整型的整数。但为什么?

Possible Duplicate:
Conditional operator cannot cast implicitly?

I have run into a peculiar situation and want to know why I have to do it. I'm using .NET 3.5.

This works:

short foo;

if (isValid)
    foo = -1;
else
    foo = getFoo();

This does not work:

short foo;
foo = isValid ? -1 : getFoo();

I have to typecast -1:

short foo;
foo = isValid ? (short)-1 : getFoo();

What does the ternary expression do differently? It considers the -1 to be an int that needs to be cast into a short. But why?

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

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

发布评论

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

评论(4

皇甫轩 2024-09-14 20:02:47

有几件事。

首先,条件运算符是 三元运算符,不是三元运算符

其次,我注意到在您的代码示例中,两个旨在等效的代码示例不是:

short foo;
if (isValid)
    foo = -1;
else
   getFoo();

与 不同

short foo = isValid ? (short)-1 : getFoo();

如果 isValid 为 false,则前者会使 foo 未分配。无论 isValid 的值如何,后者都会分配 foo。

我假设您的意思是

short foo;
if (isValid)
    foo = -1;
else
    foo = getFoo();

,而且 getFoo() 返回短。

问题是为什么条件运算符中没有类型转换的转换是非法的,但 if 语句的结果是合法的。

它在 if 语句中是合法的,因为规范第 6.1.9 节指出:

如果常量表达式的值在目标类型的范围内,则 int 类型的常量表达式可以转换为 sbyte、byte、short、ushort、uint 或 ulong 类型。

-1是int类型的常量表达式,其范围在short范围内,因此可以隐式转换为short。

那么为什么条件表达式形式是假的呢?

我们首先要明确的是,条件表达式的类型是根据其内容确定的,而不是根据其上下文确定的。赋值右侧表达式的类型并不取决于它被赋值的内容!假设你有

short M(short x){...}
int M(int x){...}

short y = M(-1);

我认为你不会期望重载解析说“好吧,我通常会选择 M(int) 因为 -1 是一个 int,但是不,我会选择 M(short) 因为否则任务不会成功。”重载解析不知道结果的去向。它的工作是根据给定的参数而不是调用的上下文来确定什么是正确的重载。

确定条件表达式的类型的工作方式相同。我们不看它的类型,我们看表达式中的类型。

好的,我们已经确定将其分配给 Short 的事实与确定表达式的类型无关。但这仍然留下了一个问题“为什么条件表达式的类型是int而不是short?”

这是一个很好的问题。让我们看看规格。

?: 运算符的第二个和第三个操作数 x 和 y 控制条件表达式的类型。

如果有类型 X 并且 y 有类型 Y 则:

如果存在从 X 到 Y 的隐式转换,但不存在从 Y 到 X 的隐式转换,则 Y 是条件表达式的类型。

如果存在从 Y 到 X 的隐式转换,但不存在从 X 到 Y 的隐式转换,则 X 是条件表达式的类型。

否则,无法确定表达式类型,并且会发生编译时错误。

在这种情况下,操作数都有一个类型。 (其中关于“if x has a type...”的措辞是针对其中有 null 或 lambda 的情况;它们没有类型!)第一个操作数是 int 类型,第二个操作数是 int 类型类型短。

存在从short 到int 的隐式转换,但不存在从int 到short 的隐式转换。因此条件表达式的类型是int,不能赋值为short。

现在,我们可以说这个算法并没有想象中那么好。我们可以使算法变得非常复杂,以处理有两种可能的“候选”类型的所有情况——在这种情况下,int和short都是可能的候选,因为当考虑为时,两个分支都可以转换为int和short特定的表达式,而不是简单地具有类型。在这种情况下,我们可以说两种类型中较小的一个是首选类型。

(有时在 C# 中,我们说两种类型中更通用的类型是更好的类型,但在这种情况下,您会希望我们选择更具体的类型。该语言在这个特定的设计方面并不一致,不幸的是,我个人宁愿我们总是选择更具体的,但在某些类型推断场景中,这将是一个重大变化。)

我早在 2006 年就考虑过这样做。在设计 LINQ 如何处理以下情况的行为时有多种类型可供选择,并且必须选择一种类型作为“最好的”我们注意到条件运算符已经必须解决这个问题,而且,在 C# 2 中它实际上并没有按照规范实现。对此进行了长时间的争论,我们最终对条件运算符的规范进行了一些细微的更改,以使其更符合其实现的(和期望的)行为。然而,当有多种类型可供选择时,我们决定不采取较大的突破性改变来调整算法以使用两种可能类型中较小的一种。

有关此问题的一些思考,请参阅我 2006 年发布的帖子:

A few things.

First off, the conditional operator is a ternary operator, not a tertiary operator.

Second, I note that in your code samples the two code samples which are intended to be equivalent are not:

short foo;
if (isValid)
    foo = -1;
else
   getFoo();

is not the same as

short foo = isValid ? (short)-1 : getFoo();

The former leaves foo unassigned if isValid is false. The latter assigns foo regardless of the value of isValid.

I assume that you meant

short foo;
if (isValid)
    foo = -1;
else
    foo = getFoo();

and that furthermore, getFoo() returns short.

The question is why the conversion in the conditional operator without the type cast is illegal but in the consequence of the if statement is legal.

It is legal in the if statement because section 6.1.9 of the specification states:

A constant-expression of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

-1 is a constant expression of type int that is in the range of short, so it can be converted to short implicitly.

So why is the conditional expression form bogus?

The first thing we have to establish clearly is the rule that the type of the conditional expression is determined from its contents, not from its context. The type of the expression on the right side of an assignment does not depend on what it is being assigned to! Suppose you had

short M(short x){...}
int M(int x){...}

short y = M(-1);

I don't think you'd expect overload resolution to say "well, I'd normally pick M(int) because -1 is an int, but no, I'll pick M(short) instead because otherwise the assignment won't work." Overload resolution doesn't know anything about where the result is going. It's job is to work out what the right overload is based on the arguments given, not based on the context of the call.

Determining the type of the conditional expression works the same way. We don't look at the type its going to, we look at the types that are in the expression.

OK, so we have established that the fact that this is being assigned to short is irrelevant for determining the type of the expression. But that still leaves the question "Why is the type of the conditional expression int rather than short?"

That is a very good question. Let's go to the spec.

The second and third operands, x and y, of the ?: operator control the type of the conditional expression.

If has type X and y has type Y then:

If an implicit conversion exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.

If an implicit conversion exists from Y to X, but not from X to Y, then X is the type of the conditional expression.

Otherwise, no expression type can be determined, and a compile-time error occurs.

In this case the operands both have a type. (The verbiage in there about "if x has a type..." is for the case where you have null or a lambda in there; those don't have types!) The first operand is of type int, the second is of type short.

An implicit conversion exists from short to int, but not from int to short. Therefore the type of the conditional expression is int, which cannot be assigned to short.

Now, one could say that this algorithm is not as good as it could be. We could greatly complicate the algorithm to deal with all the cases where there were two possible "candidate" types -- in this case, int and short are both plausible candidates because both branches are convertible to both int and short when considered as specific expressions, rather than simply as having types. We could say in that case that the smaller of the two types was the preferred type.

(Sometimes in C# we say that the more general of two types is the better type, but in this case you would want us to pick the more specific. The language is not consistent in this particular design aspect, unfortunately; I personally would rather we always choose the more specific, but there are type inference scenarios where that would be a breaking change now.)

I considered doing that back in 2006. When designing the behaviour of how LINQ deals with situations where there are multiple types to choose from and one must be picked as "the best" we noticed that the conditional operator already had to solve this problem, and that furthermore, in C# 2 it was not actually implemented according to spec. There was a long debate about this and we ended up making some minor changes to the specification for the conditional operator to bring it more into line with its implemented (and desired) behaviour. However we decided to not take the larger breaking change of tweaking the algorithm to use the smaller of two possible types when there were several to choose from.

For some musings on this problem, see my posts from 2006 on it:

枕梦 2024-09-14 20:02:47

因为-1默认是一个整数。让编译器告诉您必须明确告诉它要做什么比让编译器假设您要做什么要安全得多。

你举的例子非常简单。通过一些额外的工作,编译器可以明显地看到你希望 -1 是一个短值。然而,与隐式转换相关的所有边缘情况并不那么简单。如果您为编译器添加一条规则来对您想要的内容做出假设,则必须将其应用于每种情况而不仅仅是一种情况,这就是它变得困难的地方。

请注意,据我所知,您应该查看 Eric Lippert 博客他解释了为什么编译器不做出这样的假设。

Because -1 by default is an integer. It's a lot safer for the compiler to tell you that you have to explicitly tell it what to do, than it is for the compiler to make an assumption as to what you want done.

You're example is pretty straight forward. With a little extra work, the compiler could obviously see that you want -1 to be a short. There are all the edge cases that go along with implicit conversion that aren't so simple though. If you add a rule for the compiler to make an assumption about what you want, you have to apply it to every case not just one and that is where it gets difficult.

As a note you should check out Eric Lippert's blog as I know that he covers why the compiler doesn't make such assumptions.

皓月长歌 2024-09-14 20:02:47

应用于 intshort 的条件运算符的类型为 int;编译器不会从您分配给它的类型推断表达式的类型。

您无法将此 short 表达式隐式转换为 int

A conditional operator applied to an int and a short is of type int; the compiler will not infer the expression's type from the type you assign it to.

You cannot implicitly cast this short expression to an int.

初见你 2024-09-14 20:02:47

条件运算符强制两个可能的结果表达式具有相同的类型。在本例中,左侧是 int,右侧是 getPoo 方法返回的 short。由于将 Short 转换为 int 始终是安全的,因此编译器选择操作结果为 int

因此,结果将是将 int 分配给 short,这就是为什么您需要将其显式转换为 Short。

如果显式使用 if/else 方法,您将把一个文字整数分配给一个短整型,这允许编译器验证该文字整型是否已安全地分配给一个短整型,而无需显式强制转换。

有关内部解释,请查看:

演员不遵守分配律

The conditional operator imposes that both possible result expressions be of the same type. In this case the left side is an int and the right side is a short returned by getPoo method. Since it's always safe to convert a short to an int the compiler chooses that the result of the operation will be an int.

So the result will be the assignment of an int to a short and that's why you need to explicitly cast it to a short.

If you explicitly use an if/else approach you will be assigning a literal integer to a short which allows the compiler to verify that the literal integer is safely assigned to a short without the need of a explicit cast.

For an inside explanation take a look at:

Cast operators do not obey the distributive law

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