Структуры, интерфейсы и бокс
Возможный дубликат:
Безопасно ли для структур создавать интерфейсы?
Возьмите этот код:
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
в этом случае?
Ответы
Ответ 1
Да, это так. В принципе, всякий раз, когда вам нужна ссылка, и у вас есть значение типа значения, значение помещается в квадрат.
Здесь ISomeInterface
- это интерфейс, который является ссылочным типом. Поэтому значение someVariable
всегда является ссылкой, поэтому вновь созданное значение структуры должно быть помещено в коробку.
Ответ 2
Точка Джона - это правда, но в качестве побочного примечания есть одно небольшое исключение из правила; дженерики. Если у вас where 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
.
Ответ 3
Я добавляю это, надеюсь, пролить немного больше света на ответы, предложенные Джоном и Марком.
Рассмотрим этот не общий метод:
public static void SetToNull(ref ISomeInterface obj) {
obj = null;
}
Hmm... установка параметра ref
в значение null. Это возможно только для ссылочного типа, правильно? (Ну, или для Nullable<T>
, но не будем игнорировать этот случай, чтобы все было просто.) Таким образом, тот факт, что этот метод компилируется, говорит нам о том, что переменная, объявленная как некоторый тип интерфейса, должна рассматриваться как ссылочный тип.
Ключевая фраза здесь "объявлена как": рассмотрите эту попытку вызвать вышеупомянутый метод:
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
, ее можно установить равной нулю, чистой и простой. И это потому, что интерфейсы являются ссылочными типами - следовательно, объявляя объект как интерфейс и присваивая ему полям объектов типа значения, которые имеют значение.
Теперь, с другой стороны, рассмотрим этот гипотетический общий метод:
// 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;
}
Ответ 4
документация MSDN сообщает нам, что structs являются значениями, а не ссылочными типами. Они помещаются в коробку при преобразовании в/из переменной типа object
. Но главный вопрос здесь: как насчет переменной типа интерфейса? Так как интерфейс также может быть реализован классом, то это должно быть равносильно преобразованию из значения в ссылочный тип, как уже сказал Джон Скит, поэтому да, будет бокс. Больше обсуждений в блоге msdn.