Чтобы ответить на ваш первый вопрос: оператор as
не просто игнорирует определенные пользователем преобразования, хотя это имеет значение. Что более важно, так это то, что оператор приведения делает две противоречивые вещи. Оператор приведения означает либо:
Я знаю, что это выражение типа Foo времени компиляции будет на самом деле объектом типа Bar времени выполнения. Компилятор, я говорю вам этот факт сейчас, чтобы вы могли использовать его. Пожалуйста, сгенерируйте код, предполагая, что я прав; если я ошибаюсь, вы можете вызвать исключение во время выполнения.
Я знаю, что это выражение типа Foo времени компиляции будет иметь тип времени выполнения Foo. Существует стандартный способ преобразования некоторых или всех экземпляров Foo в экземпляр Bar. Компилятор, сгенерируйте, пожалуйста, такое преобразование, и если во время выполнения выясняется, что преобразовываемое значение не подлежит преобразованию, генерируйте исключение во время выполнения.
Это противоположности . Отличный трюк - иметь оператора, который делает противоположные вещи.
Оператор as
, напротив, имеет только первый смысл. as
только упаковывает , распаковывает и сохраняет представление преобразований. Приведение может сделать все это плюс дополнительные преобразования, изменяющие представление. Например, приведение int к short изменяет представление с четырехбайтового целого на двухбайтовое целое.
Вот почему «необработанные» приведения недопустимы для неограниченных генериков; потому что у компилятора недостаточно информации, чтобы выяснить, какой это тип приведения: бокс, распаковка, сохранение представления или изменение представления. Пользователи ожидают, что приведение в обобщенном коде имеет всю семантику приведения в более строго типизированном коде, и у нас нет способа эффективно сгенерировать этот код.
Рассмотрим:
void M<T, U>(T t, out U u)
{
u = (U)t;
}
Ожидаете ли вы, что это сработает? Какой код мы генерируем, который может обрабатывать:
M<object, string>(...); // explicit reference conversion
M<string, object>(...); // implicit reference conversion
M<int, short>(...); // explicit numeric conversion
M<short, int>(...); // implicit numeric conversion
M<int, object>(...); // boxing conversion
M<object, int>(...); // unboxing conversion
M<decimal?, int?>(...); // lifted conversion calling runtime helper method
// and so on; I could give you literally hundreds of different cases.
По сути, нам нужно было бы выдать код для теста, который снова запустил компилятор , сделал полный анализ выражений, а затем выпустил новый код. Мы реализовали эту функцию в C # 4; он называется «динамическим», и если вы хотите именно такое поведение, вы можете свободно его использовать.
У нас нет ни одной из этих проблем с as
, потому что as
делает только три вещи. Он выполняет преобразования в бокс, распаковку и тестирование типов, и мы можем легко сгенерировать код, который выполняет эти три вещи.