11

Coming from C++, I am confused as to the use of the new keyword in C#.

I understand that it doesn't work like C++'s new in the sense that you do not have to manually control the lifetime of the object, as C# has garbage collection.

However, when reading over other peoples C# code I notice statements like in code snippet 1. Wouldn't it be far easier to avoid the use of new altogether, like in code snippet 2?

Snippet 1

Foo fooInstance = new Foo();

Snippet 2

Foo fooInstance;

My question is, what are the differences between snippet 1 and snippet 2, and why should I prefer one over the other?

OMGtechy
  • 7,935
  • 8
  • 48
  • 83
  • Is snippet 2 valid C#? Genuine question, I know nothing of C#, but once asked [a related java question](http://stackoverflow.com/questions/6340535/is-the-new-keyword-in-java-redundant). – juanchopanza Apr 12 '14 at 18:43
  • @juanchopanza Sure, it's valid, it just hasn't been initialized yet. – Jeff Apr 12 '14 at 18:46
  • @Jeff Good to know C# doesn't just use `new` for fun :-) – juanchopanza Apr 12 '14 at 18:50
  • Is the `fooInstance` a field (declared on the class level) or a local variable (declared inside a method)? Is the type of `Foo` a reference type (`class`) or a value type (`struct`)? In the specific case where `fooInstance` is a field inside some class (not struct) and where `Foo` is a struct (not class), the two are really equivalent! – Jeppe Stig Nielsen Apr 12 '14 at 19:13
  • @JeppeStigNielsen Could you elaborate on that in an answer please? If it's better than the current accepted answer, I shall change it. – OMGtechy Apr 12 '14 at 19:17

6 Answers6

9

Assuming Foo is a reference type like a class, the second code snippet basically just allocates a pointer. The equivalent C++ code would be

Foo* fooInstance;

Your snippets one and two are simply not equivalent.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 2
    Thank you, although I'd assume it's more like `std::shared_ptr fooInstance;` due to the garbage collection, rather than a raw pointer? – OMGtechy Apr 12 '14 at 18:49
  • @OMGtechy Agreed, I think comparing it to a raw pointer is misleading. – Jeff Apr 12 '14 at 18:52
  • 1
    @OMGtechy The std:: smart pointers use deterministic destruction. Although quite similar in meaning to the developer, they are not quite the same as the .NET Garbage collector, which runs at some point in time... maybe tomorrow if you have enough RAM. – nvoigt Apr 12 '14 at 18:56
  • @nvoigt Sure, but on the flip side your raw pointer is a memory leak by comparison! Or it's deterministic; you can't have it both ways. – Jeff Apr 12 '14 at 18:57
  • 2
    Well, neither fits the bill perfectly. It's a garbage collected reference. C++ does not have something like that. I guess as long as there is no call to `new`we are both right and as soon as there is, we are both a little bit wrong :) – nvoigt Apr 12 '14 at 19:00
  • @nvoigt thank you for clearing that up. I'll just trust it to only free the memory when I no longer need it for now, like a super smart pointer! – OMGtechy Apr 12 '14 at 19:00
  • @OMGtechy: Rather than thinking about it as "the runtime cleans up the memory for me" (which is true), another way to think about it is "I have more memory available than I am going to use, so I don't care if I don't clean it up". Garbage collection gives you the illusion of unlimited memory. By analogy: you don't have unlimited stack space, but you also don't worry about running out of it, or about cleaning up stack space once you're done with it. Why? **because you have more than you're going to use**. Same logic. – Eric Lippert Apr 12 '14 at 20:10
4

We must discriminate between three cases:

  1. local variables
  2. (non-static) fields inside structs
  3. fields inside classes

For local variables, that is variables declared inside a method (or inside a constructor, or property/indexer/event accessor), the two are not equivalent:

class C
{
  void M()
  {
    Foo fooInstance = new Foo();
    // the variable is "definitely assigned" and can be read (copied, passed etc)
    // consider using the 'var' keyword above!
  }
}

class C
{
  void M()
  {
    Foo fooInstance;
    // the variable is not "definitely assigned", you cannot acquire its value
    // it needs to be assigned later (or can be used as 'out' parameter)
  }
}

For instance fields (non-static fields) inside a struct, only one of the "snippets" is allowed:

struct S
{
  Foo fooInstance = new Foo(); // compile-time error! cannot initialize here
}

struct S
{
  Foo fooInstance; // OK, access level is 'private' when nothing is specified
}

For fields inside a class (and static fields of a struct), the situation depends on whether Foo itself is a reference type (class) or a value type (struct or enum). The default value default(Foo) of a reference type is null, the reference that does not refer anything. The default value default(Foo) or a value type is the "instance" of the type where all fields have their default values. For value types (struct and enum), new Foo() (no arguments) and default(Foo) is the same thing. Therefore:

class C
{
  Foo fooInstance = new Foo(); // OK, private
}

class C
{
  Foo fooInstance; // OK, private
  // equivalent to 'Foo fooInstance = null;' if 'Foo' is a reference type (class, interface, delegate, array)
  // equivalent to 'Foo fooInstance = new Foo();' is 'Foo' is a value type (struct, enum)
}

It should be noted that if Foo is a reference type, the expression new Foo() is only allowed if the type actually has a constructor that takes 0 arguments, and if that constructor is accessible.

In (1) we disregarded the silly case where Foo is a struct with no instance fields.

Liam
  • 27,717
  • 28
  • 128
  • 190
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
2

The Second Creates Object of type Foo points to null in memeroy. The First points to new object using default constructor.

If you use the second and say fooInstance.SomeProperty = something. This will throw an exception as fooInstance points to null.

Zein Makki
  • 29,485
  • 6
  • 52
  • 63
2

Snippet 2 is simply declaring the reference. You haven't instantiated the object yet. If you tried to access the reference in Snippet 2 you would get a compiler error saying the value hasn't been initialized. This is different from C++ where Snippet 2 would declare a constructed object on the stack.

Jeff
  • 2,701
  • 2
  • 22
  • 35
  • That's not exactly right. You have to declare it as `Foo fooInstance = null;`, otherwise any usage of the uninitialized variable will cause a compiler error. – PMF Apr 12 '14 at 19:04
2

If you use

Foo fooInstance;

...in C#, you're just creating a reference variable on stack which points to nothing; no default constructor is called (like it would be in C++).

OMGtechy
  • 7,935
  • 8
  • 48
  • 83
Zohaib Aslam
  • 585
  • 1
  • 4
  • 15
  • 1
    Foo fooInstance is perfectly possible in C#, just don't try to access it yet :) – Jeff Apr 12 '14 at 18:47
  • yep. but it will point to nothing and when you try to call a method like "fooInstance.Show()" then an exception will be thrown. but this code will work perfectly in C++ :) – Zohaib Aslam Apr 12 '14 at 18:49
  • Thanks for your answer. It seems to be that you mean that the C++ version will call the default constructor, whilst the C# version will just create a pointer-like variable that stores nothing useful. Just a matter of rewording :) – OMGtechy Apr 12 '14 at 18:50
  • i know a lot about C#, i'm just saying that it's not useful if you use this statement "Foo fooInstance". at sometime later in your program you've to use this statement as "Foo fooInstance = new Foo()" – Zohaib Aslam Apr 12 '14 at 18:51
  • @ZohaibAslam I think the issue here is you're saying its not possible rather than not useful, otherwise I find this quite helpful! If you edit your post to reflect this, I shall upvote to get rid of the -1 and because it's a perfectly good answer (once you've incorporated those changes). :) – OMGtechy Apr 12 '14 at 18:54
  • But that can be very useful. There are plenty of instances where you may want the reference but deferred initialization. – Jeff Apr 12 '14 at 18:55
  • thank you @Dave Doknjas :) i was meant to said "not useful" :) – Zohaib Aslam Apr 12 '14 at 18:57
2

The first snippet

Foo fooInstance = new Foo();

will, as has been said, create a new instance of Foo and place a reference to it in the variable fooInstance.

For the second snippet

 Foo fooInstance; 

it depends on where it is placed:

 public class MyClass
 {
     Foo m_foo = null; // member, the "= null" part is redundant and not needed
     Foo m_foo = new Foo(); // member, initialized as part of the constructor call
     void Bar()
     {
          Foo f; // Local variable
          f.MyMethod(); // Compile time error: f is not initialized
          Foo f2=null;
          f2.MyMethod(); // Runtime error: Nullreference exception
     }
 }

Extra caution should be taken if Foo is not declared as class but as struct. Although it is also initialized using new, the instance is actually created on the stack then.

PMF
  • 14,535
  • 3
  • 23
  • 49