Ответ 1
void doFooGeneric<T>(T t) where T : I {
t.foo(); // <--- Will an S be boxed here??
}
Бокса там можно избежать!
Тип структуры S
запечатан. Для версий типа значений параметра типа T
для вашего метода doFooGeneric
выше, компилятор С# дает код, который вызывает соответствующий член структуры напрямую, без бокса.
Что круто.
См. ответ Sameer для некоторых технических деталей.
Хорошо, поэтому я придумал пример этого. Меня будут интересовать лучшие примеры, если у кого-то есть:
using System;
using System.Collections.Generic;
namespace AvoidBoxing
{
static class Program
{
static void Main()
{
var myStruct = new List<int> { 10, 20, 30, }.GetEnumerator();
myStruct.MoveNext(); // moves to '10' in list
//
// UNCOMMENT ONLY *ONE* OF THESE CALLS:
//
//UseMyStruct(ref myStruct);
//UseMyStructAndBox(ref myStruct);
Console.WriteLine("After call, current is now: " + myStruct.Current); // 10 or 20?
}
static void UseMyStruct<T>(ref T myStruct) where T : IEnumerator<int>
{
myStruct.MoveNext();
}
static void UseMyStructAndBox<T>(ref T myStruct)
{
((IEnumerator<int>)myStruct).MoveNext();
}
}
}
Здесь тип myStruct
является изменяемым типом значений, который возвращает ссылку на List<>
, а также содержит "счетчик", который запоминает, какой индекс в List<>
мы достигли до сих пор.
Мне пришлось использовать ref
, иначе тип значения будет скопирован по значению при передаче в любой из методов!
Когда я раскомментирую вызов UseMyStruct
(только), этот метод перемещает "счетчик" внутри нашего значения типа одна позиция вперед. Если он сделал это в коробке копии типа значения, мы бы не увидели его в исходном экземпляре структуры.
Чтобы узнать, какая разница в боксе, попробуйте вместо этого вызов UseMyStructAndBox
(комментарий UseMyStruct
снова). Он создает поле в роли, а MoveNext
происходит на копии. Таким образом, выход отличается!
Для тех, кто недоволен (или запутался) ref
, просто напишите Current
изнутри метода. Тогда мы можем избавиться от ref
. Пример:
static void F<T>(T t) where T : IEnumerator<int>
{
t.MoveNext(); // OK, not boxed
Console.WriteLine(t.Current);
}
static void G<T>(T t) where T : IEnumerator<int>
{
((IEnumerator<int>)t).MoveNext(); // We said "Box!", it will box; 'Move' happens to a copy
Console.WriteLine(t.Current);
}