Правила обобщения и ограничения типа
просто из любопытства, почему компилятор обрабатывает неограниченный общий тип иначе, чем typeof(object)?
class Bar { }
class Foo
{
void foo(object thing)
{
((Bar)thing).ToString();
}
}
class Foo<T>
{
void foo(T thing)
{
((Bar)thing).ToString();
}
}
в приведенном выше, приведение "t вещь" в бар приводит к ошибке компилятора. Однако кастинг "объектной вещи" на бар-это то, что компилятор позволяет мне делать, на мой собственный риск, конечно.
чего я не вижу, так это почему. В .net object в конце концов является catch-all, а тип времени выполнения может быть коробочным значением или объектом любого типа. Так Что Я ... не вижу логических причин для компилятора, чтобы различать эти два случая. Лучшее, что я могу сделать, это что-то вроде "программист ожидал бы, что компилятор выполнит проверку типов с универсальными типами, но не с объектом". :) Это все, что нужно?
кстати, я знаю, что я все еще могу получить свой бросок в случае Foo, просто написав
((Bar)(object)thing).ToString();
Я просто хочу понять, почему компилятор делает это...
2 ответов
значение здесь object
. Если первый пример был все, кроме object
он будет вести себя так же. В основном, то, что вы говорите в данный момент здесь:
(Bar)thing
это: "преобразовать T
до Bar
"; что в общем случае далеко не законно. Путем добавления object
вы делаете это:
(Bar)(object)thing
что "преобразовать T
до object
...- ...что всегда законно, поскольку ... --3--> является корнем всех управляемых типов; и обратите внимание, это может invove коробку - "...а затем бросьте object
как Bar
" - опять же; это всегда законно во время компиляции и включает проверку типа ("unbox-any") во время выполнения.
например: предположим, что T
is DateTime
...
DateTime thing = ...
Bar bar = (Bar)(object)thing;
вполне допустимо, конечно, это не во время выполнения, но это сценарий, нужно иметь в виду.
это сводится к семантике и цели создания дженериков. Если у вас есть общий тип T, компилятор не позволит вам произвольно привести его непосредственно к любому другому объекту. Это имеет смысл, поскольку цель T-заставить программиста указать, какой тип T на самом деле есть. Это не будет "объект", это будет определенный тип объекта. Во время компиляции компилятор не может знать, что будет в T, и поэтому не может бросить его.
литье из объектных работ поскольку это анонимный объект-как противоположность известному типу объекта, который определяется в его использовании.
Это может быть расширено с помощью предложения "where". Например, вы можете указать, что T должен иметь тип IBar;
interface IBar { }
class Bar : IBar { }
class Foo<T>
where T : IBar
{
void foo(T thing)
{
((IBar)thing).ToString();
}
}
наследование также работает с предложением where;
class Bar { }
class Foo<T>
where T : Bar
{
void foo(T thing)
{
// now you don't need to cast at all as the compiler knows
// exactly what type T is (at a parent level at least)
thing.ToString();
}
}