[英]Why does the compiler let me cast a null to a specific type in C#?
Consider this code: 考虑以下代码:
var str = (string)null;
When write the code this is my IL
code: 编写代码时,这是我的
IL
代码:
IL_0001: ldnull
And IL
has any Cast operator but: IL
有任何Cast操作符,但是:
var test = (string) new Object();
The IL
code is: IL
代码是:
IL_0008: castclass [mscorlib]System.String
So Casting null
to string
was ignored. 因此忽略了将
null
为string
。
Why does the compiler let me cast a null
to specific type? 为什么编译器允许我将
null
为特定类型?
In IL on this level, null
is just null
. 在此级别的IL中,
null
仅为null
。 The compiler knew it was null
because that is what you wrote, as such the compiler does not need to call the cast operator at all. 编译器知道它是
null
因为这是你写的,因此编译器根本不需要调用转换操作符。 Casting null
to an object will just yield null
. 将
null
转换为对象只会产生null
。
So this is a compile-time "optimization" or simplification if you will. 因此,如果您愿意,这是编译时的“优化”或简化。
Since this is legal, to cast null
to another object type, there is neither a warning nor an error reported from this. 由于这是合法的,要将
null
为另一个对象类型,因此既没有警告也没有报告错误。
Note that apparently the compiler will not do this even thought it may be able to verify that the value being cast is indeed guaranteed to be null
, if it isn't a literal. 请注意,显然编译器不会这样做,即使它可能能够验证正在转换的值确实保证为
null
,如果它不是文字。
Your example: 你的例子:
void Main()
{
var s = (string)null;
GC.KeepAlive(s);
}
IL: IL:
IL_0000: ldnull
IL_0001: stloc.0 // s
IL_0002: ldloc.0 // s
IL_0003: call System.GC.KeepAlive
(I added the call to GC.KeepAlive
to avoid the compiler dropping the entire variable due to it not being used anywhere.) (我添加了对
GC.KeepAlive
的调用,以避免编译器因为没有在任何地方使用而丢弃整个变量。)
If I stuff the null
into an object first, with no possibility of it changing: 如果我先将
null
填充到对象中,则不可能更改它:
void Main()
{
object o = null;
var s = (string)o;
GC.KeepAlive(s);
}
IL: IL:
IL_0000: ldnull
IL_0001: stloc.0 // o
IL_0002: ldloc.0 // o
IL_0003: castclass System.String
IL_0008: stloc.1 // s
IL_0009: ldloc.1 // s
IL_000A: call System.GC.KeepAlive
In Java there's at least one case when you need to cast a null
to some type, and that is when using overloaded methods to tell the compiler which method you want to execute (I assume this is the case in C# as well). 在Java中,至少有一种情况需要将
null
为某种类型,这就是使用重载方法告诉编译器要执行哪种方法(我假设在C#中也是这种情况)。 Since a null
is 0
(or whatever pointer null
represents) no matter what type it is you won't see any difference in the compiled code though (apart from which method was called). 由于
null
为0
(或任何指针null
表示),无论它是什么类型,您都不会看到编译代码中的任何差异(除了调用哪个方法)。
Because the spec says so. 因为规范说的如此。 See §6.1.5, §6.2 and §7.7.6 of the C# 5 standard .
参见C#5标准的 §6.1.5,§6.2和§7.7.6。 To quote only the relevant parts:
仅引用相关部分:
§7.7.6 Cast expressions
§7.7.6转换表达式
A cast-expression of the form
(T)E
, whereT
is a type andE
is a unary-expression , performs an explicit conversion (§6.2) of the value ofE
to typeT
.形式的铸表达式
(T)E
,其中T
是一种类型和E
是一元表达式 ,执行显式转换(第6.2节)的值的E
键入T
。 [... T]he result is the value produced by the explicit conversion.[... T]他的结果是显式转换产生的值。
§6.2 Explicit conversions
§6.2明确的转换
The following conversions are classified as explicit conversions:
以下转化归类为显式转化:
- All implicit conversions.
所有隐式转换。
§6.1.5 Implicit reference conversions
§6.1.5隐式引用转换
The implicit reference conversions are:
隐式引用转换是:
- From the null literal to any reference-type .
从null文字到任何引用类型 。
Casting of a null is perfectly valid - sometimes it is required when passing arguments to overloaded methods in order to inform the compiler which method is being invoked. 抛出null是完全有效的 - 有时在将参数传递给重载方法时是必需的,以便通知编译器正在调用哪个方法。
See the following related question: 请参阅以下相关问题:
强制转换的目的是将str
定义为String类型,因此它不是关于是否可以将null转换为Type,而是关于定义变量类型的更多信息。
Not everything that looks like (SomeType)expression
is really a cast, in C#. 并非所有看起来像
(SomeType)expression
都是C#中的(SomeType)expression
。 Sometimes the expression needs to acquire a type that it didn't have already. 有时候表达式需要获得一个它已经没有的类型。 Some other examples:
其他一些例子:
var a = (short)42;
var b = (Func<int, int>)(i => i + 1);
var c = (IConvertible)"Hello";
In each of these cases, one could also have written the type on the left, instead of var
, if one preferred that. 在每种情况下,如果有人愿意,也可以在左边写一个类型,而不是
var
。 But this is not always the case when the expression is a part of a larger expression, say: 但是当表达式是更大表达式的一部分时,情况并非总是如此,例如:
CallOverloadedMethod((short)42); // CallOverloadedMethod has another overload that we don't want
var y = b ? x : (IConvertible)"Hello"; // the ?: operator might not be able to figure out the type without this hint
In your example the literal null
has no type in itself. 在您的示例中,文字
null
本身没有类型。 In some cases, like when choosing between many overloads, like when using the ternary ?:
operator, or when declaring a variable with var
syntax, it is necessary to have an expression that is still null
but also carries a type. 在某些情况下,比如在多次重载之间进行选择时,例如在使用三元
?:
运算符时,或者在使用var
语法声明变量时,必须使表达式仍然为null
但也带有一个类型。
Note that overloads also includes operators, for example in: 请注意,重载还包括运算符,例如:
public static bool operator ==(Giraffe g1, Giraffe g2)
{
if (g1 == (object)null && g2 != (object)null
|| g1 != (object)null && g2 == (object)null)
{
return false;
}
// rest of operator body here ...
}
the (object)null
syntax is used to ensure the user-defined overload of ==
is not called recursively. (object)null
语法用于确保递归调用==
的用户定义的重载。
Your syntax is correct and there is no specification limitations in c#. 您的语法是正确的,c#中没有规范限制。 These are specification rules:
这些是规范规则:
The implicit reference conversions are:
隐式引用转换是:
From any reference-type to object and dynamic.
从任何引用类型到对象和动态。
From any class-type S to any class-type T, provided S is derived from T.
从任何类型S到任何类型类型T,只要S来自T.
From any class-type S to any interface-type T, provided S implements T.
从任何类型S到任何接口类型T,只要S实现T.
From any interface-type S to any interface-type T, provided S is derived from T.
从任何接口类型S到任何接口类型T,只要S来自T.
From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: o S and T differ only in element type.
从具有元素类型SE的数组类型S到具有元素类型TE的数组类型T,提供以下所有条件:o S和T仅在元素类型上不同。 In other words, S and T have the same number of dimensions.
换句话说,S和T具有相同的维数。 o Both SE and TE are reference-types.
o SE和TE都是参考类型。 o An implicit reference conversion exists from SE to TE.
o从SE到TE存在隐式引用转换。
From any array-type to System.Array and the interfaces it implements.
从任何数组类型到System.Array及其实现的接口。
From a single-dimensional array type S[] to System.Collections.Generic.IList and its base interfaces, provided that there is an implicit identity or reference conversion from S to T.
从一维数组类型S []到System.Collections.Generic.IList及其基接口,前提是存在从S到T的隐式标识或引用转换。
From any delegate-type to System.Delegate and the interfaces it implements.
从任何委托类型到System.Delegate及其实现的接口。
From the null literal to any reference-type.
从null文字到任何引用类型。
From any reference-type to a reference-type T if it has an implicit identity or reference conversion to a reference-type T0 and T0 has an identity conversion to T.
从任何引用类型到引用类型T,如果它具有隐式标识或引用转换为引用类型T0并且T0具有到T的标识转换。
From any reference-type to an interface or delegate type T if it has an implicit identity or reference conversion to an interface or delegate type T0 and T0 is variance-convertible (§13.1.3.2) to T.
从任何引用类型到接口或委托类型T,如果它具有隐式标识或引用转换到接口或委托类型T0和T0是方差可转换(第13.1.3.2节)到T.
Implicit conversions involving type parameters that are known to be reference types.
涉及已知为引用类型的类型参数的隐式转换。 See §6.1.10 for more details on implicit conversions involving type parameters.
有关涉及类型参数的隐式转换的更多详细信息,请参见第6.1.10节。 The implicit reference conversions are those conversions between reference-types that can be proven to always succeed, and therefore require no checks at run-time.
隐式引用转换是引用类型之间的转换,可以证明它们总是成功,因此在运行时不需要检查。 Reference conversions, implicit or explicit, never change the referential identity of the object being converted.
隐式或显式的引用转换永远不会更改要转换的对象的引用标识。 In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.
换句话说,虽然引用转换可能会更改引用的类型,但它永远不会更改所引用对象的类型或值。
Suppose that compiler does return a warning for var str = (string)null;
假设编译器确实返回
var str = (string)null;
的警告var str = (string)null;
. 。 So should this line be alerted?:
var str = SomeFunctionThatReturnsNull()
While using the var compiler must know what type it's supposed to initialize at compile time, and also it doesn't matter if you are going to put a null value in it as long as it's declared as a nullable type. 那么该行应该被警告吗?:
var str = SomeFunctionThatReturnsNull()
虽然使用var编译器必须知道它应该在编译时初始化的类型,并且如果你要在其中放入一个空值也没关系只要它被声明为可以为空的类型。 It's no surprise to see that compiler is not calling Cast in null case because there is nothing to be cast. 看到编译器没有调用Cast in null的情况并不奇怪,因为没有任何东西可以被强制转换。
I think you should read specification.You can cast a null value to every you want. 我认为你应该阅读specification.You可以为你想要的每个人施加一个空值。 See it:
看见:
• From the null literal to any reference-type.
•从null文字到任何引用类型。
Just before use the value you check for null. 在使用之前,您检查的值为null。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.