Смущает мало используемая инициализация типа значения - PullRequest
6 голосов
/ 22 июня 2011

Следующий код недопустим:

public struct MyStruct
{
    public MyStruct(int a, int b)
    {
        this.a = a;
        this.b = b;
    }

    public int a;
    public int b;
}

//now I want to cache for whatever reason the default value of MyStruct
MyStruct defaultValue;
...
if (foo != defaultValue) //use of unassigned variable...

Очевидно, что все должно быть сделано:

MyStruct defaultValue = default(MyStruct) //or new MyStruct();
...
if (foo != defaultValue) //compiler is happy

Но также допускается следующее (Iя не знал об этом и случайно наткнулся на него):

MyStruct defaultValue;
defaultValue.a = 0;
defaultValue.b = 0;
...
if (foo != defaultValue) //legal

Я думаю, компилятор проверяет, что все поля struct были инициализированы, и поэтому позволяет этому коду компилироваться.Тем не менее я нахожу это путающим с тем, как работает остальная часть языка.В конце концов, вы в основном используете неназначенную переменную в C # способе видеть вещи.

Вещи становятся еще более запутанными, если учесть следующий код:

public struct MyStruct
{
    public MyStruct(int a, int b)
    {
        this.a = a;
        this.b = b;
        this.c = (a + b).ToString();
    }

    public int a;
    public int b;
    internal string c;
}

Следующий код недопустимпотому что в этом случае мы не присвоили все видимые поля:

MyStruct defaultValue;
defaultValue.a = 0;
defaultValue.b = 0;
...
if (foo != defaultValue) //use of unassigned variable...

Visible является ключевым словом здесь, потому что, если MyStruct должны были быть определеныв ссылочной сборке c не будет видимым , и компилятор не будет жаловаться, и предыдущий код будет совершенно корректным.Снова сбивает с толку.

Может кто-нибудь объяснить, почему это позволило инициализировать struct в C # таким образом?Почему бы не запретить это полностью, чтобы при работе с любым типом значения в языке был более унифицированный опыт?

EDIT 1 : я допустил ошибку в последнем примере.Компилятор будет счастлив, только если поле not visible имеет тип reference .Еще более запутанно.Является ли этот последний случай известной ошибкой в ​​компиляторе или есть разумная причина, по которой он работает так, как работает?

Изменен последний пример на допустимый.

EDIT 2: Я все еще немного озадачен тем, как работает инициализация value-type .Почему, например, не допускается следующее:

struct MyStruct
{ 
    public int A { get; set; } //Auto-implemented property
    public int B { get; set; } //Auto-implemented property
}

MyStruct defaultValue;
defaultValue.A = 0;  //use of unassigned variable...
defaultValue.B = 0;

На мой взгляд, нет никаких сомнений в том, что все поля MyStruct инициализированы.Я вижу причину, по которой это нельзя было бы разрешить, если бы свойства не были автоматически реализованы , поскольку вполне возможно, что установщики не гарантируют, что все поля установлены.Но в автоматически реализованных свойствах компилятор знает со 100% -ной уверенностью, что поля будут установлены, если свойства заданы (после всего кода, который генерирует для вас компилятор).

Наконец, небольшой теоретический случай, который, очевидно, не имеет практического применения:

struct MyUselessStruct
{
}

MyUselessStruct defaultValue;
Console.WriteLine(defaultValue.ToString()); //hehe did I get away with using an unassigned variable?

Тогда почему это не разрешено:

Object object;
if (object == null) .... //use of unassigned variable

Я нахожу оба случая схожими по концепции иЯ ожидаю, что они оба будут работать одинаково в C #.Я до сих пор не понимаю, почему это, казалось бы, бесполезное различие в способе инициализации переменных тип значения и его практическое применение (помимо несоответствий, которые я объяснил в первой части моего вопроса)

1 Ответ

5 голосов
/ 22 июня 2011

Спецификация явно позволяет это; 12,3 в ECMA334v4

  • Переменная struct-type считается определенно назначенной, если каждая из ее переменных экземпляра считается определенно назначенной.

Тем не менее, изменчивые структуры являются злом . Поэтому я настоятельно рекомендую вам НЕ делать это.

Сделайте поля приватными только для чтения, установите их через пользовательский конструктор и получите доступ к ним через get -only свойство:

public struct MyStruct
{
    public MyStruct(int a, int b)
    {
        this.a = a;
        this.b = b;
    }
    public int A { get { return a; } }
    public int B { get { return b; } }
    private readonly int a, b;
    internal int C { get { return a + b; } }
}
...