为什么这段代码在 C# 中无效?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/202271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is this code invalid in C#?
提问by mmattax
The following code will not compile:
以下代码将无法编译:
string foo = "bar";
Object o = foo == null ? DBNull.Value : foo;
I get: Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'System.DBNull' and 'string'
我得到:错误 1 条件表达式的类型无法确定,因为“System.DBNull”和“string”之间没有隐式转换
To fix this, I must do something like this:
为了解决这个问题,我必须做这样的事情:
string foo = "bar";
Object o = foo == null ? DBNull.Value : (Object)foo;
This cast seems pointless as this is certainly legal:
这个演员似乎毫无意义,因为这当然是合法的:
string foo = "bar";
Object o = foo == null ? "gork" : foo;
It seems to me that when the ternary branches are of different types, the compiler will not autobox the values to the type object...but when they are of the same type then the autoboxing is automatic.
在我看来,当三元分支的类型不同时,编译器不会将值自动装箱到类型对象......但是当它们属于相同类型时,自动装箱是自动的。
In my mind the first statement should be legal...
在我看来,第一个声明应该是合法的......
Can anyone describe why the compiler does not allow this and why the designers of C# chose to do this? I believe this is legal in Java...Though I have not verified this.
谁能描述为什么编译器不允许这样做以及为什么 C# 的设计者选择这样做?我相信这在 Java 中是合法的...虽然我没有验证过这一点。
Thanks.
谢谢。
EDIT:I am asking for an understanding of why Java and C# handle this differently, what is going on underneath the scenes in C# that make this invalid. I know how to use ternary, and am not looking for a "better way" to code the examples. I understand the rules of ternary in C#, but I want to know WHY...
编辑:我要求了解为什么 Java 和 C# 以不同的方式处理这个问题,在 C# 的场景下发生了什么使这个无效。我知道如何使用三元,并且不是在寻找“更好的方法”来编写示例代码。我了解 C# 中三元的规则,但我想知道为什么...
EDIT(Jon Skeet): Removed "autoboxing" tag as no boxing is involved in this question.
编辑(Jon Skeet):删除了“自动装箱”标签,因为此问题不涉及装箱。
回答by Keith
DBNull.Value
returns type DBNull
.
DBNull.Value
返回类型DBNull
。
You want the type to be string
.
您希望类型为string
.
While string
can be null
it cannot be a DBNull
.
虽然string
可以是null
它不能是DBNull
.
In your code the statement on the right of the equals executes before assignment to the object.
在您的代码中,equals 右侧的语句在分配给对象之前执行。
Basically if you use:
基本上,如果您使用:
[condition] ? true value : false value;
In .Net both the true and false options need to be implicitly convertible to the same type, before whatever you assign them to.
在 .Net 中,true 和 false 选项都需要隐式转换为相同类型,然后再分配给它们。
This is a result of how C# deals with type-safety. For instance the following is valid:
这是 C# 如何处理类型安全的结果。例如以下是有效的:
string item = "item";
var test = item != null ? item : "BLANK";
C#3 doesn't support dynamic types, so what is test? In C# every assignment is also a statement with a return value, so although the var
construct is new in C#3 the statement on the right of the equals always has to resolve to a single type.
C#3 不支持动态类型,那么什么是测试?在 C# 中,每个赋值也是一个带有返回值的语句,因此尽管var
构造在 C#3 中是新的,但等于右侧的语句始终必须解析为单一类型。
In C#4 and above you can explicitly support dynamic types, but I don't think that helps here.
在 C#4 及更高版本中,您可以显式支持动态类型,但我认为这没有帮助。
回答by Jon Skeet
The compiler requires that either the types of second and third operands are the same, or that one is implicitly convertible to the other. In your case, the types are DBNull and string, neither of which is implicitly convertible to the other. Casting either of them to object solves that.
编译器要求第二个和第三个操作数的类型相同,或者一个可以隐式转换为另一个。在您的情况下,类型是 DBNull 和字符串,它们都不能隐式转换为另一个。将它们中的任何一个投射到对象都可以解决这个问题。
EDIT: Looks like it is indeed legal in Java. Quite how it works out what to do when it comes to method overloading, I'm not sure... I've just looked at the JLS, and it's extremely unclear about what the type of the conditional is when there are two incompatible reference types involved. The C# way of working may be more irritating occasionally, but it's clearer IMO.
编辑:看起来它在 Java 中确实是合法的。当涉及到方法重载时,它是如何确定该怎么做的,我不确定......我刚刚查看了 JLS,当有两个不兼容的引用时,非常不清楚条件的类型是什么涉及的类型。C# 的工作方式有时可能更令人恼火,但 IMO 更清晰。
The relevant section of the C# 3.0 spec is 7.13, the conditional operator:
C# 3.0 规范的相关部分是 7.13,条件运算符:
The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,
- If X and Y are the same type, then this is the type of the conditional
- Otherwise, if an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
- Otherwise, if an implicit conversion (§6.1) 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.
?: 运算符的第二个和第三个操作数控制条件表达式的类型。设 X 和 Y 是第二个和第三个操作数的类型。然后,
- 如果 X 和 Y 是相同的类型,那么这是条件的类型
- 否则,如果存在从 X 到 Y 的隐式转换(第 6.1 节),但不存在从 Y 到 X 的隐式转换,则 Y 是条件表达式的类型。
- 否则,如果存在从 Y 到 X 的隐式转换(第 6.1 节),但不存在从 X 到 Y 的隐式转换,则 X 是条件表达式的类型。
- 否则,无法确定表达式类型,并发生编译时错误。
回答by Konrad Rudolph
By the way, your code is a special case that doesn't have to use the conditional operator at all. Instead, the null coalesce operator is more appropriate (but still requires casting):
顺便说一下,您的代码是一种特殊情况,根本不必使用条件运算符。相反,空合并运算符更合适(但仍需要强制转换):
object result = (object)foo ?? DBNull.Value;