Возможный дубликат:
Для структур действительно ли безопасно реализовать интерфейсы?Возьмите этот код:
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
упакованный в этом случае?
Да, это так. Обычно, когда вам нужна ссылка и у вас есть только значение типа значения, это значение помещается в рамку.
Здесь ISomeInterface
- это интерфейс, который является ссылочным типом. Следовательно, значение someVariable
всегда является ссылкой, поэтому вновь созданное значение структуры должно быть помещено в рамку.
Я добавляю это, чтобы, надеюсь, пролить немного больше света на ответы, предложенные Джоном и Марком.
Рассмотрим этот неуниверсальный метод:
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;
}
В документации MSDN говорится, что структуры являются значениями, а не ссылочными типами. Они помещаются в рамку при преобразовании в / из переменной типа , объект
. Но главный вопрос здесь: как насчет переменной типа интерфейса? Поскольку интерфейс также может быть реализован классом, это должно быть равносильно преобразованию из значения в ссылочный тип, как уже сказал Джон Скит, поэтому да, упаковка будет иметь место. Дополнительное обсуждение в блоге msdn .
Точка зрения Джона верна, но в качестве примечания к правилу есть одно небольшое исключение; дженерики. Если у вас есть , где 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
.