Почему "где T: класс" требуется в этом примере?
Образец кода:
using System.Collections.Generic;
using FileHelpers;
....
private void Save<T>(string destFilename, IEnumerable<T> data) where T : class
{
var engine = new FileHelperEngine((typeof(T)));
engine.HeaderText = engine.GetFileHeader();
engine.WriteFile(destFilename, data); // XX
}
В строке XX второй параметр для engine.WriteFile ожидает IEnumerable<объект>. Этот код работает нормально.
Мой вопрос: почему для метода требуется ограничение "где T: класс"? Если я удаляю его, я получаю ошибку времени компиляции:
Argument 2: cannot convert from
'System.Collections.Generic.IEnumerable<T>' to
'System.Collections.Generic.IEnumerable<object>'
Я бы подумал, что все было "объектом", и поэтому ограничение не было необходимости?
2 ответа
Ограничение необходимо, потому что object
только ссылочный тип; типы значений причины могут быть назначены object
из-за бокса (хотя технически все типы наследуются от System.Object
).
Но бокс - это отдельная проблема от дисперсии параметров типа; IEnumerable<T>
с неограниченным Т не может быть преобразован в IEnumerable<object>
потому что дисперсия не поддерживается для типов значений.
Как в сторону, FileHelperEngine<T>
, который не является родовым FileHelperEngine
наследует от (как FileHelperEngine<object>
), также имеет T : class
ограничение. Таким образом, вы не пропускаете никакой функциональности, имея ограничение, так как в любом случае поддерживаются только ссылочные типы - теоретически можно просто использовать FileHelperEngine<T>
непосредственно, не проходя через неуниверсальный класс, поскольку метод, приведенный в примере, уже является универсальным:
using System.Collections.Generic;
using FileHelpers;
....
private void Save<T>(string destFilename, IEnumerable<T> data) where T : class
{
var engine = new FileHelperEngine<T>();
engine.HeaderText = engine.GetFileHeader();
engine.WriteFile(destFilename, data);
}
Вы собираетесь подать заявку T
в FileHelperEngine<T>
который уже имеет такое же ограничение, T : class
, Компилятор может сказать, что если вы не наложите такое же ограничение на ваш метод, то T
может быть недействительным для FileHelperEngine<T>
, Так что это просто предотвращает несоответствия типов.