Search code examples
c#structheap-memoryinstantiationstack-memory

Why does adding primitive struct to a List not require the new keyword. Whereas adding non-primitive struct to List require the new keyword? - C#


Adding primitive struct (eg. int) to a List:

int i=10;
List<int> list=new List<int>();
list.Add(i);

Versus: Adding non-primitive struct (eg. KeyValuePair<int,int>) to a list:

List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.Add(new KeyValuePair<int,int>(10,20));

While adding the int struct to a list, we do not need to use the new keyword. But while adding the KeyValuePair struct to a list, we need to use the new keyword.

I mean to say, the following statement is not valid:

list.Add(new int(10)); //invalid statement

Though both int and KeyValuePair are struct's, they behave differently - one does not require instantiation before use (as far as the user is concerned). And the other requires instantiation before use.

Why can't we do the following instead:

list.Add(KeyValuePair<int,int>(10,20)) //omit the new keyword, as we were doing with an int

Coming from a C/C++ background, what does the new keyword exactly do in C#? Does it just instantiate the underlying data type (and we are not sure whether the instantiated data type will lie on the Stack or the Heap). Or, are we sure that using the new keyword will allocate memory on the Heap (as it does in C++)?


Solution

  • what does the new keyword exactly do in C#?

    It's all listed here. The most relevant one to this question is "constructor invocation". Structs and classes have constructors, and constructors create instances of structs and classes.

    When you do:

    new KeyValuePair<int,int>(10,20)
    

    you are calling this constructor.

    int, which is an alias for the Int32 struct, does not have a constructor that accepts a parameter of type int. This is the reason why you can't do:

    new int(10)
    

    Note that calling a constructor isn't the only way to create an instance of a struct. You can also do something like:

    var defaultKVP = default(KeyValuePair<int, int>); // gets the default value of the type KeyValuePair<int, int>
    // defaultKVP is an instance of KeyValuePair<int, int>! It's not null! Structs can't be null :)
    

    The default value of a struct is defined by setting all its value-typed fields to their default values, and reference-typed fields to null.

    The reason why an integer literal like 10 is an instance of the struct Int32, is, well, compiler magic. The spec says so, so it is implemented this way.