Есть ли бокс /Unboxing при бросании структуры в общий интерфейс?

Возможный дубликат:
Структуры, интерфейсы и бокс

Из MSDN: http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx

Бокс - это процесс преобразования типа значения в объект типа или любой тип интерфейса, реализованный этим типом значения.

Но как насчет общих интерфейсов?

Например, int происходит как от IComparable, так и от IComparable<int>.

Скажем, у меня есть следующий код:

void foo(IComparable value)    { /* etc. */ }
void bar(IComparable<T> value) { /* etc. */ }

void gizmo()
{
   int i = 42;

   bar(i); // is `i` boxed? I'd say YES
   foo(i); // is `i` boxed? I fear it is (but I hope for NO)
}

Есть ли bar (или любая функция, использующая не общий интерфейс) означает, что будет бокс?

Есть ли foo (или любая функция, использующая общий интерфейс для типа), означает, что будет бокс?

Спасибо.

Ответы

Ответ 1

Всякий раз, когда структура передается в интерфейс, она помещается в коробку. Цель IComparable <T> что-то вроде:

void bar<T>(T value) where T : IComparable<T> { /* etc. */ }

При использовании таким образом структура будет передаваться как структура (через параметр типового типа), а не как интерфейс, и, следовательно, ее не нужно будет вставлять в бокс. Обратите внимание, что в зависимости от размера структуры иногда бывает лучше пройти по значению, а иногда и по ссылке, хотя, конечно, если вы используете существующий интерфейс, такой как IComparable, который должен пройти по мере необходимости интерфейса.

Ответ 2

Во-первых, короткий (и, вероятно, неполный) праймер на типах значений, ссылочных типах и боксе.

Вы можете сказать, что что-то является типом значения, потому что изменения, сделанные в функции, не сохраняются вне функции. Значение объекта копируется при вызове функции и выбрасывается в конце этой функции.

Вы можете сказать, что что-то является ссылочным типом, потому что изменения, сделанные в функции, сохраняются вне функции. Значение объекта не копируется при вызове функции и существует после окончания этой функции.

Если что-то помещено в коробку, делается одна копия и помещается в ссылочный тип. Он эффективно изменяется от типа значения к ссылочному типу.

Обратите внимание, что все это относится к состоянию instanced, то есть к любым нестационарным данным элемента. Статические члены не имеют статус состояния и не имеют ничего общего со ссылочными типами, типами значений или боксом. Методы и свойства, которые не используют состояние instanced (например, те, которые используют только локальные переменные или статические данные элемента), не будут работать по-разному по отношению к ссылочным типам, типам значений или при возникновении бокса.

Вооружившись этим знанием, вот как мы можем доказать, что бокс происходит при преобразовании структуры в интерфейс (общий или нет):

using System;

interface ISomeInterface<T>
{
    void Foo();
    T MyValue { get; }
}

struct SomeStruct : ISomeInterface<int>
{
    public void Foo()
    {
        this.myValue++;
    }

    public int MyValue
    {
        get { return myValue; }
    }

    private int myValue;
}

class Program
{
    static void SomeFunction(ISomeInterface<int> value)
    {
        value.Foo();
    }

    static void Main(string[] args)
    {
        SomeStruct test1 = new SomeStruct();
        ISomeInterface<int> test2 = test1;

        // Call with struct directly
        SomeFunction(test1);
        Console.WriteLine(test1.MyValue);
        SomeFunction(test1);
        Console.WriteLine(test1.MyValue);

        // Call with struct converted to interface
        SomeFunction(test2);
        Console.WriteLine(test2.MyValue);
        SomeFunction(test2);
        Console.WriteLine(test2.MyValue);
    }
}

Результат выглядит следующим образом:

0
0
1
2

Это означает, что бокс происходит только при выполнении преобразования:

  • Первые два вызова делают бокс при каждом вызове.
  • Вторые два вызова уже имеют коробчатую копию, и бокс не возникает при каждом вызове.

Я не буду пытаться дублировать весь код здесь, но если вы измените ISomeInterface<T> на ISomeInterface, у вас все равно будет такое же поведение.

Ответ 3

Сводка ответов

Моя путаница в отношении общих интерфейсов и бокса/распаковки произошла из-за того, что я знал, что генераторы С# позволили нам создавать более эффективный код.

Например, факт int реализует IComparable<T> и IComparable для меня:

  • IComparable должен был использоваться со старым, пред-генерическим кодом, но означал бы бокс/распаковку
  • IComparable<T> должен был использоваться для генерируемого кода, предположительно избегая бокса/распаковки

Комментарий Эрика Липперта такой же простой, понятный и прямой, как это может быть:

Типичными типами интерфейсов являются типы интерфейсов. Там нет ничего особенного в том, что волшебным образом предотвращает бокс

Теперь я уверен, что приведение структуры в интерфейс будет означать бокс.

Но тогда, как IComparable<T> должен работать более эффективно, чем IComparable?

Вот где ответ supercat (под редакцией Лассе В. Карлсен) указал мне на то, что дженерики скорее напоминают шаблоны на С++, чем я думал:

Цель IComparable - разрешить что-то вроде:

   void bar<T>(T value) where T : IComparable<T> { /* etc. */ }

Что сильно отличается от:

   void bar(IComparable<T> value) { /* etc. */ }

Или даже:

   void bar(IComparable value) { /* etc. */ }

Я предполагаю, что для первого прототипа среда выполнения будет генерировать одну функцию для каждого типа и, таким образом, избегать проблем с боксом при работе с структурами.

В то время как для второго прототипа среда выполнения будет генерировать только функции с интерфейсом в качестве параметра, и, таким образом, делать бокс, когда T является структурой. Третья функция просто вставляет структуру, не более, не менее.

(я предполагаю, что здесь генерируются С# в сочетании с С# -структурами, демонстрирующими свое превосходство по сравнению с реализацией дженериков Java-типа).

Ответ Мерлин Морган-Грэм предоставил мне пример теста, который я буду играть дома. Я завершу это резюме, как только у меня появятся значимые результаты (думаю, я попытаюсь использовать семантику pass-by-reference, чтобы увидеть, как все это работает...)