Бокс при использовании дженериков в С#
У меня есть следующий простой код С#:
private Stack<Person> m_stack = new Stack<Person>();
public void Add<T>(T obj)
where T : Person
{
m_stack.Push(obj);
}
Это приведет к следующему IL-коду:
.method public hidebysig instance void
Add<(ConsoleApplication1.Person) T>(!!T obj) cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [System]System.Collections.Generic.Stack`1<class ConsoleApplication1.Person> ConsoleApplication1.Pool::m_stack
IL_0007: ldarg.1
IL_0008: box !!T
IL_000d: callvirt instance void class [System]System.Collections.Generic.Stack`1<class ConsoleApplication1.Person>::Push(!0)
IL_0012: nop
IL_0013: ret
} // end of method Pool::Add
Итак, мой вопрос: почему бокс? (IL_0008) Я могу понять downcasting или даже скомпилировать ошибку, но почему бокс (Person является ссылочным типом...)
Спасибо заранее!
Ответы
Ответ 1
Выдержка из раздела Ecma-335 III 4.1
Если типTok является ссылочным типом, команда box ничего не делает.
где typeTok - это!! T в вашем случае.
Я предполагаю, что когда компилятор компилирует код, он всегда вызывает поле независимо от того, является ли тип операнда ссылочным типом или нет. Из-за семантики команды box желаемый результат всегда гарантируется.
Ответ 2
Я считаю, что это ограничение общего метода, которое делает это с вами, но...
В любом случае вообще не требуется общий метод. Просто напишите его как:
public void Add(Person person)
{
m_stack.Push(person);
}
Вы обнаружите, что IL упрощается и полностью избегает проблемы. Если вы ограничены определенным типом ссылки, вы можете просто использовать этот тип ссылки.
Это намного легче понять и гораздо более ясно. Я бы предложил избегать вызова метода generic, если он действительно не нужен. Общие методы делают класс менее очевидным, что означает, что он менее читабельный и поддерживаемый в долгосрочной перспективе и более сложный в использовании.