简体   繁体   中英

Difference between A a =new A() and A a=null

In C#,

A a = new A();
A a = null;
A a;

How does these 3 lines work with respect to memory?

I know the first line will create a memory in heap, but what about rest two lines?

How it work if, A a ; is a field and local variable.

  1. Creates a new instance of A and assigns it to the variable a .
  2. Does nothing. It just assigns null to a reference a . If a is not used, the compiler might optimize it away.
  3. Does nothing too. It will revert to A a = default(A); which is the same as 2 since default(A) is null . For method variables it will show you a warning or error if you don't assign it. This one can too be optimized away if not used.

A a = new A(); This actually instantiates a new object of type A . a is the reference to the object. a is stored on the stack while the actual object is stored on the heap.

A a = null; just creates the reference on the stack - no data on the heap.

A a; this I believe is the same as A a = null; - EDIT Clarification from OP required on context of the question.

It depends on the context , if A a is a field , eg

  class MyClass {
    ...
    // A a is field of some class/structure
    A a = new A(); // A a = null; or A a;
    ...
  }

so

  A a = new A();

is a field a of type A with new instance of A as an initial value; and these two lines are equal (field a of type A with initial null value):

  A a = null; 
  A a;

since

"The initial value of a field, whether it be a static field or an instance field, is the default value"

https://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx

In case of local variable , eg

  public void MyMethod() {
    ...
    // A a is a local variable in some method
    A a = new A(); // A a = null; or A a;
    ...
  }

compiler doesn't initailize local variables

https://msdn.microsoft.com/en-us/library/4y7h161d(v=vs.71).aspx

so

  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

Assuming that A is a reference type and that this code is in a method:

A a = new A(); will always create a new object on the heap, and assign to a a reference to that new object.

A a = null; and A a; will both assign null to a .

However, there can be a difference in the IL generated for A a = null; compared to A a;

Consider the following simple program:

static void Main()
{
    string s;

    if (Environment.TickCount > 0)
        s = "A";
    else
        s = "B";

    Console.WriteLine(s);
}

The IL generated for a release build looks like this:

.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 
}

Now modify the code to initialise the reference to null:

static void Main()
{
    string s = null;

    if (Environment.TickCount > 0)
        s = "A";
    else
        s = "B";

    Console.WriteLine(s);
}

And the IL changes to this:

.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 

Note that two new IL instructions have been generated to initialise the variable to null (even though, as far as I know, .locals init ([0] string s) would already have initialised it to null).

It may well be that the JIT compiler will optimise this away, but there is certainly a difference in terms of the IL code generated.

(I used string for simplicity in this example, but the same happens if you use a class of your own.)

Second and third statement are the same, ie allocate an empty reference, pointing to "null"; the first one will allocate a new instance of A class in managed heap, and assign its address to a reference variable

All the three constructs allocate a reference on stack for the locally scoped variable a.

  1. A a = new A(); constructs an object on heap (assuming A is a class, because it's reference can be assigned a null).

This construct is useful if your team's guidelines favor explicit type declarations over var . You would use a var otherwise in this case, and the type is inferred by the compiler:

var a = new A();
  1. A a = null; assigns null reference to a, which is useful if you are going to introduce a read access to the reference.

For example:

Func<int, int> factorial = null;
factorial = n => n < 3 ? n : n * factorial(n-1);

The lambda expression's scope inherits the scope of the variable declaration, so you need a variable initialization. Without = null it's considered a compiler error, because it introduces a read access to a delegate, you don't have a non-null for it just yet, and don't want to make it up.

This construct is useful because you can't infer a type of null. You could use var a = default(A); instead, which would work both for value types as well.

  1. A a ;

Just declares a variable. If you need to introduce a read access to the variable, you must assign it in the scope before. Basically you can't use it anyway, so no reason to declare it that way. A general rule is to declare a variable closer to the first usage.

A valid use case for such a construct to appear in your code is only during an automated refactoring (ReSharper), when using an Split declaration and assignment refactoring, which would convert a var to A , immediately followed by a Move to outer scope refactoring. Say you have a variable x of type SomeVerylongTypeName<EvenMoreLongTypeName> which is of an inferred type, and want to get the type name.

var x = container.GetFirst(); // some imaginary GetFirst method which returns an instance of non-keyboard friendly type.

You can just use a following sequence of keys: var a = x; (Left)(Left)(Left) (Alt-Enter)...Split declaration and assignment...(Enter), and you get your explicit declaration in the editor:

SomeVerylongTypeName<EvenMoreLongTypeName> a;
a = x;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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