C# A=new A()和A=null之间的差异
在C#中 这三条线是如何与记忆相关的 我知道第一行将在堆中创建内存,但剩下的两行呢 如果, A A;C# A=new A()和A=null之间的差异,c#,heap-memory,C#,Heap Memory,在C#中 这三条线是如何与记忆相关的 我知道第一行将在堆中创建内存,但剩下的两行呢 如果, A A; 是一个字段和局部变量 第二条和第三条语句相同,即分配一个空引用,指向“null”; 第一个将在托管堆中分配一个类的新实例,并将其地址分配给一个引用变量 创建a的新实例,并将其分配给变量a 什么也不做。它只是将null分配给引用a。如果未使用a,编译器可能会对其进行优化 什么也不做。它将恢复为A=默认值(A)与2相同,因为默认值(A)为null。对于方法变量,如果您不分配它,它将向您显示警告或错误
是一个字段和局部变量 第二条和第三条语句相同,即分配一个空引用,指向“null”; 第一个将在托管堆中分配一个类的新实例,并将其地址分配给一个引用变量
a
的新实例,并将其分配给变量a
null
分配给引用a
。如果未使用a
,编译器可能会对其进行优化A=默认值(A)
与2相同,因为默认值(A)
为null
。对于方法变量,如果您不分配它,它将向您显示警告或错误。如果不使用,这个也可以优化掉A A=新A()代码>
这实际上实例化了一个a类型的新对象。a是对对象的引用。a存储在堆栈上,而实际对象存储在堆上
A=null代码>只在堆栈上创建引用-堆上没有数据
A代码>我认为这与A=null相同代码>-编辑根据问题的上下文要求OP进行澄清。如果A
是一个字段,例如
所以
是一个a
类型的字段a
,其初始值为a
的新实例;这两行相等(类型为a
的字段a
,初始值为null
值):
自
字段的初始值,无论是静态字段还是静态字段
实例字段,是默认值“
如果是局部变量,例如
编译器不初始化局部变量
所以
假设A
是引用类型,并且此代码位于方法中:
A A=新A()
将始终在堆上创建一个新对象,并为a
分配对该新对象的引用
A=null代码>和A
会将null
分配给a
但是,为a a=null生成的IL可能存在差异代码>与A比较代码>
考虑以下简单程序:
A a = new A(); // "a" of type "A" declaration with new instance of A as an initial value
A a = null; // "a" of type "A" declaration with null initial value
A a; // just "a" declaration, "a" contains trash and should be initialized before using
为发布版本生成的IL如下所示:
static void Main()
{
string s;
if (Environment.TickCount > 0)
s = "A";
else
s = "B";
Console.WriteLine(s);
}
现在修改代码以将引用初始化为null:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] string s)
L_0000: call int32 [mscorlib]System.Environment::get_TickCount()
L_0005: ldc.i4.0
L_0006: ble.s L_0010
L_0008: ldstr "A"
L_000d: stloc.0
L_000e: br.s L_0016
L_0010: ldstr "B"
L_0015: stloc.0
L_0016: ldloc.0
L_0017: call void [mscorlib]System.Console::WriteLine(string)
L_001c: ret
}
IL对此进行了更改:
static void Main()
{
string s = null;
if (Environment.TickCount > 0)
s = "A";
else
s = "B";
Console.WriteLine(s);
}
.method private hidebysing static void Main()cil managed
{
.入口点
.maxstack 2
.init(
[0]字符串(s)
L_0000:ldnull所有三个构造都在堆栈上为局部作用域变量a分配一个引用
A=newa();
在堆上构造一个对象(假设A是一个类,因为它的引用可以赋值为null)
如果您的团队指南支持显式类型声明而不是var
,则此构造非常有用。在这种情况下,您可以使用var
,否则,编译器将推断类型:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] string s)
L_0000: ldnull <====== Lookie here
L_0001: stloc.0 <====== and here
L_0002: call int32 [mscorlib]System.Environment::get_TickCount()
L_0007: ldc.i4.0
L_0008: ble.s L_0012
L_000a: ldstr "A"
L_000f: stloc.0
L_0010: br.s L_0018
L_0012: ldstr "B"
L_0017: stloc.0
L_0018: ldloc.0
L_0019: call void [mscorlib]System.Console::WriteLine(string)
L_001e: ret
A A=null;
将null引用分配给A,如果要引入对引用的读取访问,这非常有用
例如:
var a = new A();
您可以只使用以下键序列:
var a=x;(左)(左)(左)(Alt-Enter)…拆分声明和赋值…(Enter),然后在编辑器中获得显式声明:
var x = container.GetFirst(); // some imaginary GetFirst method which returns an instance of non-keyboard friendly type.
SomeVerylongTypeName a;
a=x;
什么是空引用,它将如何映射??它们不一样,因为A A=null;A b=A;
不会导致编译错误,但A A;A b=A;
会导致编译错误。从内存分配的角度来看,它们是相同的。编译器会在为引用赋值之前阻止您使用它,但是内存分配没有任何变化P问“这三行是如何与内存相关的?”请解释为什么在代码中,您可以轻松返回第2行而无需编辑它,但是第3行它会抱怨未初始化的变量。我建议您也可以查看此处的信息以了解有关null的更多信息:因为它未初始化,所以它会向您显示警告或错误(它不知道它是什么)。因为使用2时,您确实分配了一个值:null
(它知道它是什么:它没有值)。当我说“抱怨”时,我的意思是它不允许您编译。所以(至少编译时间),第2行和第3行不是同一件事。@BjarkeSøgaard第2行和第3行是相同的,如果A是类,并且声明是类中的字段,因为字段(与局部变量相反)是否默认初始化,这相当于将其置零。cf.您是否声明了字段或局部变量?最后一行在这两种情况下的行为会有所不同。a
a是类还是结构?请注意,您正在变得糟糕(IMO)因为你的问题不够清楚,所以答案是假设性的。上下文在这里有很大的不同。@Jon你能澄清一下吗?希望能从你这里学到一些东西:)@JonSkeet我的意思是将类a声明为字段和本地两种情况。然后你应该编辑你的问题来显示这一点,包括字段是否是字段结构或类的引用。回答您问题的人不必阅读注释来理解您的问题。引用仅当是局部变量或结构中的字段(也在堆栈上)时才会存储在堆栈上-在这种情况下,您的最终语句是不正确的。您是对的,但在第三行中,您说a
与A=null
相同。块中的某些部分不能使用A.Anything
,因为它会在编译时表示未实例化变量,但如果将其赋值为null和
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] string s)
L_0000: call int32 [mscorlib]System.Environment::get_TickCount()
L_0005: ldc.i4.0
L_0006: ble.s L_0010
L_0008: ldstr "A"
L_000d: stloc.0
L_000e: br.s L_0016
L_0010: ldstr "B"
L_0015: stloc.0
L_0016: ldloc.0
L_0017: call void [mscorlib]System.Console::WriteLine(string)
L_001c: ret
}
static void Main()
{
string s = null;
if (Environment.TickCount > 0)
s = "A";
else
s = "B";
Console.WriteLine(s);
}
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] string s)
L_0000: ldnull <====== Lookie here
L_0001: stloc.0 <====== and here
L_0002: call int32 [mscorlib]System.Environment::get_TickCount()
L_0007: ldc.i4.0
L_0008: ble.s L_0012
L_000a: ldstr "A"
L_000f: stloc.0
L_0010: br.s L_0018
L_0012: ldstr "B"
L_0017: stloc.0
L_0018: ldloc.0
L_0019: call void [mscorlib]System.Console::WriteLine(string)
L_001e: ret
var a = new A();
Func<int, int> factorial = null;
factorial = n => n < 3 ? n : n * factorial(n-1);
var x = container.GetFirst(); // some imaginary GetFirst method which returns an instance of non-keyboard friendly type.
SomeVerylongTypeName<EvenMoreLongTypeName> a;
a = x;