4

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.

keerti_h
  • 383
  • 2
  • 13
  • 5
    Are you declaring a field or a local variable? The last line will behave differently in the two cases. Also, is `A` a class or a struct? – Jon Skeet Nov 16 '15 at 10:12
  • It's equivalent to (1) `int i = 1;` (2) `int i = 0;` or (3) `int i;` (minus the dynamic allocation in (1) if A is a class). – Peter - Reinstate Monica Nov 16 '15 at 10:12
  • 2
    Note that you're getting bad (IMO) answers which make assumptions because you haven't been clear enough in your question. The context makes a *big* difference here. – Jon Skeet Nov 16 '15 at 10:14
  • 2
    @Jon could you clarify that? Hope to learn something from you here :) – Patrick Hofman Nov 16 '15 at 10:16
  • @JonSkeet Doesn't assigning/initializing with null rule A being a struct out? – Peter - Reinstate Monica Nov 16 '15 at 10:20
  • @PeterSchneider: Not if there's a user-defined implicit conversion, e.g. from `string` to `A`.... (Unlikely, I know - but just another example of where it helps to be really complete.) – Jon Skeet Nov 16 '15 at 10:22
  • [this article](http://www.yoda.arachsys.com/csharp/memory.html) covers a lot of the complexities quite well – Liam Nov 16 '15 at 11:03
  • @JonSkeet Lets consider both case local and global. will both be in stack? here i considered as Class. – keerti_h Nov 16 '15 at 11:04
  • I don't know what you mean by "global" nor what you mean by "both". I suggest you edit your question to make everything clear. You should read http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx btw. – Jon Skeet Nov 16 '15 at 11:04
  • 1
    @JonSkeet I mean declaring class A as field and local both case. – keerti_h Nov 16 '15 at 11:10
  • 1
    Then you should edit your question to show that, including whether the field is a field of a struct or a class. Someone answering your question shouldn't have to read the comments to understand what you're asking. – Jon Skeet Nov 16 '15 at 11:11

6 Answers6

4
  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.
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Please explain why, in code, you can easily return line 2 without editing it, however line 3 it will complain about an uninitialized variable. – Falgantil Nov 16 '15 at 10:11
  • I would suggest to also look at info here to get more informed about null: https://msdn.microsoft.com/en-us/library/edakx9da.aspx – Denis Kralj Nov 16 '15 at 10:11
  • Since it isn't initialized, it shows you a warning or error (it doesn't know what it is). Since with 2 you do assign a value: `null` (it knows what it is: it doesn't have a value). – Patrick Hofman Nov 16 '15 at 10:12
  • When I said 'complain' I meant it will not allow you to compile. So (at least compile-time), line 2 and line 3 are not the same thing. – Falgantil Nov 16 '15 at 10:18
  • @BjarkeSøgaard Line 2 and 3 are identical if A is a class and the declarations are fields in a class, because fields (as opposed to local variables) are default-initalized, which amounts to nulling them. cf https://msdn.microsoft.com/en-us/library/aa645756%28v=vs.71%29.aspx. – Peter - Reinstate Monica Nov 16 '15 at 10:24
1

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.

Kieran Quinn
  • 1,085
  • 2
  • 22
  • 49
  • 1
    The reference is only stored on the stack if it's a local variable or a field in a struct which is also on the stack - in which case your final statement is incorrect. – Jon Skeet Nov 16 '15 at 10:13
  • You are right, but in third line you say that `A a` is same as `A a = null`.Somethere in the block you can't use `a.Anything` because it will say at compile time UnInstantiated Variable, but if you assign it null and will use in the same way it will throw exception NullReferenceException.Only reason is this – Suren Srapyan Nov 16 '15 at 10:15
1

It depends on the context, if A a is a field, e.g.

  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, e.g.

  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
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
1

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

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • the reference to type's default might differ between local variables and class fields, static fields... etc. also I wouldnt take string as a reference point, not saying here it generated wrong result, but its definitely a specific type thanks to ex. string interning :) – mikus Nov 16 '15 at 11:08
  • @mikus I tested this with a custom class, and the same happens, so I decided to keep using a string in the example for brevity. Note my last sentence in my answer. – Matthew Watson Nov 16 '15 at 11:16
0

Second and third statement are the same, i.e. 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

Gian Paolo
  • 4,161
  • 4
  • 16
  • 34
0

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;
George Polevoy
  • 7,450
  • 3
  • 36
  • 61
  • 1
    var is not obligatory. In fact the coding guidelines in my company expressly forbid it – Liam Nov 16 '15 at 10:57
  • 1
    you can just ask resharper to specify the type for you, no need to do all the magic with outer scope to get it :) Almost always it's a proposed refactoring for 'var' keyword. I also donte agree with the statement that A a = new A() is useless, var is not obligatory, and keeping full type on the left side not only increases readibility in many cases, but also can help avoiding some nasty errors, where ex. you initialize 'a' with a method return value, then you change its return type, in a way that used method signatures still match and therefore code compiles, but its not the type you wanted – mikus Nov 16 '15 at 11:00
  • @Liam Thanks, Updated the answer for those using explicit type declarations. – George Polevoy Nov 16 '15 at 12:25
  • @mikus According to Liskov's substitution principle you should not worry about that. – George Polevoy Dec 01 '15 at 20:04
  • @mikus As for the 'Specify Type Explicitly' refactoring, it would not help answering point 3 from original question. – George Polevoy Dec 01 '15 at 20:10
  • @GeorgePolevoy, Liskov principle has nothing to do with it, it applies to polimorphism, what I am talking about is that you change existing variable type, ex. from DTO to full entity model, or Contact to Company. Now using var, if you initialize the object with this new type, it will compile as long as new type specifies similar interface (let's say you used CreatedDate and Name properties). If you used fully specified type name, a compilation error would occur.With var you'll learn in a year maybe. I don't mean its a wrong concept, i love it, but its not uiltimate solution. – mikus Dec 03 '15 at 11:09
  • @mikus We are actually talking about a concept which is coherent to polymorphism. This kind of of implicit type dependency and is one of most productive things in modern languages, which in my experience outweighs the problems you describe. I don't see a reason to debate about var here, please check out http://stackoverflow.com/questions/41479/use-of-var-keyword-in-c-sharp – George Polevoy Dec 03 '15 at 11:41