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.
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.
A
and assigns it to the variable a
.null
to a reference a
. If a
is not used, the compiler might optimize it away.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, 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
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, 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
All the three constructs allocate a reference on stack for the locally scoped variable a.
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();
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.
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;