简体   繁体   English

为什么编译器允许我在C#中将null转换为特定类型?

[英]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. 因此忽略了将nullstring

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). 由于null0 (或任何指针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 , where T is a type and E is a unary-expression , performs an explicit conversion (§6.2) of the value of E to type T . 形式的铸表达式 (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: 请参阅以下相关问题:

Casting null as an object? 将null转换为对象?

强制转换的目的是将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.

相关问题 为什么 C# 编译器允许使用 Linq 而不是使用括号执行强制转换? - Why does the C# compiler allow a cast to be performed with Linq but not with parentheses? 为什么我的 C# 编译器 (Visual Studio) 不允许我使用 try 块执行此操作? - Why doesn't my C# compiler (Visual Studio) let me do this with a try block? 在将Int64转换为Int32时,为什么C#让我溢出而没有任何错误或警告?它是如何进行转换的? - Why does C# let me overflow without any error or warning when casting an Int64 to an Int32 and how does it do the cast? 为什么 C# 编译器认为这个空包引用可以为空? - Why does C# compiler thinks this nullbale reference can be null? 在C#中为什么我可以将接口转换为任何类型而没有编译器错误 - in c# why I can cast an interface to any type without a compiler error 如何在 C# 中输入 Cast null 作为 Bool? - How to Type Cast null as Bool in C#? 为什么C#编译器允许在IEnumerable <T>和TAlmostAnything之间进行显式转换? - Why does the C# compiler allow an explicit cast between IEnumerable<T> and TAlmostAnything? 为什么 C# 不知道如何排序时让我编译排序代码 - Why does C# let me compile sort code when it doesnt know how to sort C#编译器如何处理重载显式转换运算符? - How does the C# compiler handle overloading explicit cast operators? 为什么C#编译器不调用隐式转换运算符? - Why C# compiler doesn't call implicit cast operator?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM