Подтвердить что ты не робот

С# struct new StructType() vs default (StructType)

Скажем, у меня есть структура

public struct Foo
{
    ...
}

Есть ли разница между

Foo foo = new Foo();

и

Foo foo = default(Foo);

?

4b9b3361

Ответ 1

Вы можете удивиться, почему, если они точно такие же, есть два способа сделать одно и то же.

Они не совсем одинаковы, потому что каждый ссылочный тип или тип значения гарантированно имеет значение по умолчанию, но не каждый тип ссылки гарантированно имеет конструктор без параметров:

static T MakeDefault<T>()
{
    return default(T); // legal
    // return new T(); // illegal
}

Ответ 2

Нет, оба выражения дадут тот же точный результат.

Так как structs не может содержать явные конструкторы без параметров (т.е. вы не можете определить их самостоятельно), конструктор по умолчанию предоставит вам версию структуры со всеми значениями zero'd out. Это то же поведение, что и default дает вам.

Ответ 3

Для типов значений варианты, фактически говоря, эквивалентны.

Однако я был заинтригован эмпирическими исследованиями Jon Skeet , в которых "инструкции" приводят к вызову конструктивного конструктора по умолчанию, когда он указанный в CIL (вы не можете сделать это на С#, потому что это не позволяет вам). Среди прочего он пробовал default(T) и new T(), где T - это параметр типа. Они оказались эквивалентными; ни один из них не вызывал конструктора.

Но один случай (кажется), который он не пробовал, был default(Foo), где Foo является фактическим struct type.

Итак, я взял его код для "взломанной" структуры и попробовал это для себя.


Оказывается, что default (Foo) не вызывает конструктор, тогда как новый Foo() на самом деле делает.

Использование типа struct Oddity, который указывает конструктор без параметров:

При отключенных оптимизации метод:

private void CallDefault()
{
    Oddity a = default(Oddity);
}

создает CIL (без nop s, ret и т.д.):

L_0001: ldloca.s a
L_0003: initobj [Oddity]Oddity

тогда как метод:

private void CallNew()
{
    Oddity b = new Oddity();
}

дает:

L_0001: ldloca.s b
L_0003: call instance void [Oddity]Oddity::.ctor()

С оптимизацией, включенной, компилятор, похоже, оптимизирует почти весь метод CallDefault в no-op, но сохраняет вызов конструктора в CallNew (для потенциального побочные эффекты?).

Ответ 4

Спецификация языка (§4.1.2 и п. 5.2) - ваш друг. В частности:

Для переменной типа значения значение по умолчанию такое же, как значение, вычисленное конструктором по умолчанию для типов значений (§ 4.1.1).

(курсив в оригинале.)

Обратите внимание, что это не то же самое для ссылочных типов.

Для переменной ссылочного типа значение по умолчанию null.

Это сильно отличается от значения, созданного конструктором по умолчанию, если он существует.

Ответ 5

Ключевое слово

default полезно, когда вы не знаете точный тип и работает не только для структур, например, в дженериках:

T FirstOrDefault(IEnumerable<T> source)
{
    if (...source is empty...) return default(T);
}

Это возвращает значение null для ссылочных типов, значение по умолчанию для примитивных типов (0 для чисел, false для bool), стандартно инициализированная структура и т.д.

Когда тип известен во время компиляции, нет смысла использовать default, вместо него можно использовать new Foo()