繁体   English   中英

在.Net / C#中,是否为强类型?

[英]In .Net/C#, is null strongly-typed?

null是否有类型? 如何在内部表示空值? 以下代码中发生了什么?

void Foo(string bar) {...}
void Foo(object bar) {...}

Foo((string)null);

编辑:迄今为止的答案都是非特定的,而且太高级了。 我知道引用类型对象由堆栈上的指针组成,指针指向堆上的一个位置,该位置包含同步块索引,类型句柄和对象的字段。 当我将对象的实例设置为null ,堆栈上的指针指向哪里? 而在代码片段,仅仅是使用C#编译器来决定哪些超载致电剧组,并没有真正的空的事情进行任何造型?

我正在寻找了解CLR内部人员的深入答案。

代码示例中的转换为string不会为null提供类型,因为null本身不能具有类型。 如果你想要证明这一点,那么执行下面的代码,你可以看到null总是等于它自己,不管它被赋给的变量类型是什么:

string s = null;
IPAddress i = null;
Console.WriteLine(object.Equals(s, i)); // prints "True"
Console.WriteLine(object.ReferenceEquals(s, i)); // prints "True"

演员所做的是告诉编译器选择哪个重载。 由于null没有类型,因此不知道是否选择带有objectstring的重载,因为该值可以解释为。 因此,您可以通过说“这是一个应该被视为字符串的空值”来帮助它。


如果你想看看下面发生了什么,那么从代码中查看IL。 方法调用的相关位在文本IL中类似于以下内容(取决于您的命名空间和类名等):

ldnull 
call void ConsoleApplication1.Program::Foo(string)

所有发生的事情是在堆栈上加载null,然后由占用字符串的重载消耗,因为在编译时执行重载解析,因此调用的方法被烘焙到IL中。

如果你想看看ldnull做了什么,以及为什么它只是使用像ldc.i4.0这样的东西将零加载到堆栈上然后看到这个答案 (如果你不想关注链接,原因是那个它是一个与大小无关的零,否则在CLR中不存在)。

null没有类型,并且“在代码片段中,C#编译器只是使用它来决定调用哪个重载,并且实际上没有任何转换为​​null?” 正是这样。 让我们看一下生成的IL。

  IL_0001:  ldnull
  IL_0002:  call       void ConsoleApplication1.Program::Foo(string)

注意ldnull将null加载到堆栈。 它只是null,不是作为字符串或其他东西的null。 重要的是第二行,其中IL显式调用接收字符串的重载。 如果你打电话给对象,会发生以下情况:

  IL_0001:  ldnull
  IL_0002:  call       void ConsoleApplication1.Program::Foo(object)

所以,是的,强制转换是一个C#工件,因此编译器知道要调用的重载。

它将调用Foo(字符串栏)。

你可以抛空null。

将引用对象设置为null ,指针(后面)指向内存中指定为null的特殊位置。 因此,当您转换为null ,您真正创建了一个指向null的特定Type的指针。

正如其他人(1) (2)所证明的那样,情况似乎并非如此,因为编译器根据特定的null来解决发出IL的重载。

我知道引用变量有一个与之关联的类型。 我认为这将是这个null背后但我错了。

ldnull操作码将空引用(类型O)推送到堆栈上。 并且,即使此null引用具有与之关联的Type,该call接受此引用作为string的有效参数。 至少,这是我对它的理解。 如果有人对此有任何更正,请随时纠正我。

null关键字是表示空引用的文字,不引用任何对象。 null是引用类型变量的默认值。 来自MSDN 所以你的例子中String的默认值。

// A null string is not the same as an empty string.
string s = null;
string t = String.Empty; // Logically the same as ""

你正在做的相当于按照上面的使用默认值:

int equal = string.Compare((string)null, default(string));

NULL是标记,它没有数据类型。当您将.NULL分配给字段或变量时,该值将更改为NULL,但字段或变量的数据类型不会更改。

在我的示例中将字符串类型指定为null的原因我认为是为了显示将使用witch方法(在这种情况下为“void Foo(string bar){...}”,因为这是接受字符串作为参数的方法。)

如果你会调用Foo((object)null); 将使用另一种方法。(“void Foo(object bar){...}”)

ECMA-334定义了null值的类型,如下所示:

11.2.7 null类型

null literal(§9.4.4.6)求值为null值,该值用于表示不指向任何对象或数组的引用,或者缺少值。 null类型具有单个值,即null值。 因此,类型为null类型的表达式只能计算null值。 无法显式写入null类型,因此无法在声明的类型中使用它。

null类型是类型层次结构的底部类型 - 与object相反; null类型可以被认为是每个可空类型的子类型,因为null值可以在任何可以为空的表达式出现的地方使用。

这确实在类型系统中创建了一个弱点,因为它意味着其接收器是可空类型的任何操作都可以为null,并且操作可能在运行时因异常而失败。 在强类型系统中,如果接收器属于该类型,则保证由类型提供的操作(即,您不会获得空指针异常,这实际上是说null类型没有实现您所使用的任何方法在它上面调用,虽然null值可以来自你认为你正在使用的任何类型的表达式)。

在您的代码中,方法被重载,并且产生参数的表达式的静态类型用于重载解析。 由于强制转换为字符串,表达式具有类型字符串(null类型是字符串的子类型,根据C#类型系统,这是一个上传,所以是安全的)。 作为第一个近似值,编译器选择最具体的重载可见,因此选择Foo的字符串版本而不是对象版本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM