Структуры, интерфейсы и упаковывающий [копируют]

Возможный дубликат:
Для структур действительно ли безопасно реализовать интерфейсы?

Возьмите этот код:

interface ISomeInterface
{
    public int SomeProperty { get; }
}

struct SomeStruct : ISomeInterface
{
    int someValue;

    public int SomeProperty { get { return someValue; } }

    public SomeStruct(int value)
    {
        someValue = value;
    }
}

и затем я делаю это где-нибудь:

ISomeInterface someVariable = new SomeStruct(2);

SomeStruct упакованный в этом случае?

43
задан Community 23 May 2017 в 12:34
поделиться

4 ответа

Да, это так. Обычно, когда вам нужна ссылка и у вас есть только значение типа значения, это значение помещается в рамку.

Здесь ISomeInterface - это интерфейс, который является ссылочным типом. Следовательно, значение someVariable всегда является ссылкой, поэтому вновь созданное значение структуры должно быть помещено в рамку.

54
ответ дан 26 November 2019 в 22:30
поделиться

Я добавляю это, чтобы, надеюсь, пролить немного больше света на ответы, предложенные Джоном и Марком.

Рассмотрим этот неуниверсальный метод:

public static void SetToNull(ref ISomeInterface obj) {
    obj = null;
}

Хм... установка нулевого параметра ref . Это возможно только для ссылочного типа, верно? (Ну, или для Nullable ; но давайте проигнорируем этот случай, чтобы не усложнять задачу.) Таким образом, тот факт, что этот метод компилируется, говорит нам, что переменная, объявленная как относящаяся к некоторому типу интерфейса, должна быть обработана как ссылочный тип.

Ключевая фраза здесь «объявлен как»: рассмотрите эту попытку вызвать вышеуказанный метод:

var x = new SomeStruct();

// This line does not compile:
// "Cannot convert from ref SomeStruct to ref ISomeInterface" --
// since x is declared to be of type SomeStruct, it cannot be passed
// to a method that wants a parameter of type ref ISomeInterface.
SetToNull(ref x);

Конечно, причина, по которой вы не можете передать x в приведенном выше коде в SetToNull заключается в том, что x необходимо объявить как ISomeInterface , чтобы вы могли передать ref x - а не ], потому что компилятор волшебным образом знает, что SetToNull включает строку obj = null . Но в некотором смысле это только усиливает мою точку зрения: строка obj = null допустима именно потому, что было бы незаконным передавать переменную не объявлен как ISomeInterface к методу.

Другими словами, если переменная объявлена ​​как ISomeInterface , она может иметь значение NULL в чистом виде. И это потому, что интерфейсы являются ссылочными типами - следовательно, объявление объекта как интерфейса и присвоение его объекту типа значения блокирует это значение.

Теперь, с другой стороны, рассмотрим этот гипотетический универсальный метод:

// This method does not compile:
// "Cannot convert null to type parameter 'T' because it could be 
// a non-nullable value type. Consider using 'default(T)' instead." --
// since this method could take a variable declared as, e.g., a SomeStruct,
// the compiler cannot assume a null assignment is legal.
public static void SetToNull<T>(ref T obj) where T : ISomeInterface {
    obj = null;
}
9
ответ дан 26 November 2019 в 22:30
поделиться

В документации MSDN говорится, что структуры являются значениями, а не ссылочными типами. Они помещаются в рамку при преобразовании в / из переменной типа , объект . Но главный вопрос здесь: как насчет переменной типа интерфейса? Поскольку интерфейс также может быть реализован классом, это должно быть равносильно преобразованию из значения в ссылочный тип, как уже сказал Джон Скит, поэтому да, упаковка будет иметь место. Дополнительное обсуждение в блоге msdn .

0
ответ дан 26 November 2019 в 22:30
поделиться

Точка зрения Джона верна, но в качестве примечания к правилу есть одно небольшое исключение; дженерики. Если у вас есть , где T: ISomeInterface , то это с ограничениями и использует специальный код операции . Это означает, что интерфейс можно использовать без упаковки . Например:

public static void Foo<T>(T obj) where T : ISomeInterface {
    obj.Bar(); // Bar defined on ISomeInterface
}

Это не включает упаковку, даже для типа значения T . Однако, если (в том же Foo ) вы выполните:

ISomeInterface asInterface = obj;
asInterface.Bar();

, то это будет как раньше. с ограничениями только применяется непосредственно к T .

67
ответ дан 26 November 2019 в 22:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: