Is this a bug in the C# 4.0 compiler?
Asked Answered
I

7

16

This code compiles successfully, but I think it should fail to compile. Also, when you run it you get a NullReferenceException. The missing code is the "new Bar" in the initialization of the Bar property.

class Bar
{
    public string Name { get; set; }
}

class Foo
{
    public Bar Bar { get; set; }
}


class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo
                      {
                          Bar = { Name = "Hello" }
                      };
    }
}

Is this a known bug?

Interlaminate answered 1/10, 2010 at 14:19 Comment(3)
Why do you think it should fail to compile? I would not assume it's a compiler bug off the bat.Latrinalatrine
Because there's no way this can ever workGladisgladney
@Maxm007: How do you know it will never work? The compiler cannot proffer up an error unless the compiler has an algorithm for determining when to do so. Precisely what set of circumstances do you think should be an error?Thunell
H
41

Why do you think it should fail to compile? It is nested object initializer syntax, and it is the responsibility of the client code to provide a valid value for initialization.

From the documentation:

C# spec 7.5.10.2 "Object initializers"

A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property

Hayley answered 1/10, 2010 at 14:23 Comment(1)
Great description of the issue.Busey
L
16

No this is not a bug.

If you want it to run you either put a new before Bar (just like you did for Foo before the initializer) or you create the Bar object in Foo's constructor.

The object initializer is essentially just syntactic sugar.

This:

var foo = new Foo
            {
                Bar = { Name = "Hello" }
            };

Is exactly the same as this:

var foo = new Foo();
foo.Bar.Name = "Hello"; 
Lyndseylyndsie answered 1/10, 2010 at 14:26 Comment(0)
G
3

The new is unecessary in an object initializer:

object-creation-expression:
    new   type   (   argument-list(opt)   )   object-or-collection-initializer(opt) 
    new   type   object-or-collection-initializer

object-or-collection-initializer:
    object-initializer
    collection-initializer

object-initializer:
    {   member-initializer-list(opt)   }
    {   member-initializer-list   ,   }

initializer-value:
    expression
    object-or-collection-initializer

It's that last one that is most important. It represents the right-hand-side of your property = value syntax. This means that the right-hand-side can be a normal c# expression (with a new operator) or another initalizer. In which case, all you need are the opening and closing braces.

Girlie answered 1/10, 2010 at 14:24 Comment(4)
Yeah, understood about the new not being necessary but where is the type for bar?Gladisgladney
It's inferred because the type is known based on the property being initialized.Girlie
so if it inferred it its a bar, and then assumes its New Bar , why does it throw nullrefexception.Gladisgladney
Ahhh got it. If you preinitialize bar it will work. I did not know you could use the initializer for existing instancesGladisgladney
B
2

If you change your code to the following equivalent, you will also get a runtime error of a NullReferenceException instead of a compile time error/warning.

static void Main(string[] args) {
    Foo foo2 = new Foo();
    foo2.Bar.Name = "test";
}

The effect is the same, Bar is never properly initialized. Now, from a compiler writers perspective it is extremely difficult to determine in all cases as to whether Bar was properly initialized prior to use.

Busey answered 1/10, 2010 at 14:33 Comment(0)
N
2
...
 Bar = { Name = "Hello"}
...

means: Foo.Bar.Name="Hello" not: {Foo.Bar=new Bar(); Foo.Bar.Name="Hello";}

This will compile and will not throw any exception, so it's not a bug, you're just initializing an unexisting object:

class Bar
{
    public string Name;
}
class Foo
{
private Bar _bar = new Bar();
public Bar Bar
{
  get { return _bar; }
  set { _bar = value; }
}
}
class Program
{
 static void Main(string[] args)
 {
  Foo foo = new Foo
  {
   Bar = { Name = "Hello"}
  };
 }
}
Nudicaul answered 1/10, 2010 at 14:47 Comment(0)
A
1

Bar is a property of Foo, so it is allowing you to access it and assigning a name property at compile time, but at run time it checks for the valid instance of Bar which is not present so throwing null reference exception, it will be the case with any C# version.

Anorthosite answered 1/10, 2010 at 14:25 Comment(0)
S
0

I create a working sample.

Its easy, only add a "new Bar()" an it work fine


class Bar
{
    public string Name { get; set; }
}

class Foo
{
    public Bar Bar { get; set; }
}


class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo
                      {
                          Bar = new Bar() { Name = "Hello" }
                      };

        Console.WriteLine(foo.Bar.Name);
        Console.ReadLine();
    }
}
Scrip answered 5/10, 2010 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.