Это ошибка в компиляторе C # 4.0? - PullRequest
16 голосов
/ 01 октября 2010

Этот код успешно компилируется, но я думаю, что он не должен компилироваться. Кроме того, когда вы запускаете его, вы получаете NullReferenceException. Отсутствующий код - это «новая полоса» при инициализации свойства Bar.

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" }
                      };
    }
}

Это известная ошибка?

Ответы [ 7 ]

41 голосов
/ 01 октября 2010

Почему вы думаете, что он не должен компилироваться?Это вложенный синтаксис инициализатора объекта, и клиентский код отвечает за предоставление допустимого значения для инициализации.

Из документации:

C # spec 7.5.10.2 "Objectinitializers "

Инициализатор члена, который задает инициализатор объекта после знака равенства, является инициализатором вложенного объекта, то есть инициализацией внедренного объекта.Вместо назначения нового значения полю или свойству, назначения в инициализаторе вложенного объекта обрабатываются как присвоения членам поля или свойства

16 голосов
/ 01 октября 2010

Нет, это не ошибка.

Если вы хотите, чтобы он запускался, либо поставьте new перед Bar (как вы делали для Foo до инициализатора), либо создайте объект Barв конструкторе Foo.

Инициализатор объекта - это просто синтаксический сахар.

Это:

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

Точно так же, как это:

var foo = new Foo();
foo.Bar.Name = "Hello"; 
3 голосов
/ 01 октября 2010

В инициализаторе объекта нет необходимости new:

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

Это последний, который является наиболее важным.Он представляет правую часть вашего синтаксиса property = value.Это означает, что с правой стороны может быть нормальное выражение c # (с оператором new) или другого инициализатора.В этом случае все, что вам нужно, это открывающие и закрывающие скобки.

2 голосов
/ 01 октября 2010
...
 Bar = { Name = "Hello"}
...

означает: Foo.Bar.Name="Hello" not: {Foo.Bar=new Bar(); Foo.Bar.Name="Hello";}

Это скомпилирует и не вызовет никаких исключений, так что это не ошибка, вы просто инициализируете несуществующий объект:

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"}
  };
 }
}
2 голосов
/ 01 октября 2010

Если вы измените свой код на следующий эквивалент, вы также получите ошибку времени выполнения исключения NullReferenceException вместо ошибки / предупреждения времени компиляции.

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

Эффект тот же, Бар никогда не инициализируется должным образом.Теперь, с точки зрения авторов компиляторов, чрезвычайно трудно определить во всех случаях, была ли Bar должным образом инициализирована перед использованием.

1 голос
/ 01 октября 2010

Bar - это свойство Foo, поэтому оно позволяет вам получить к нему доступ и назначить свойство name во время компиляции, но во время выполнения оно проверяет действительный экземпляр Bar, которого нет бросая исключение нулевой ссылки, это будет иметь место с любой версией C #.

0 голосов
/ 05 октября 2010

Я создаю рабочий образец .

Легко, только добавьте "новый бар ()" и он прекрасно работает


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();
    }
}
...