Expression.Convert не генерирует InvalidOperationException для параметров типа инвариантного значения? - PullRequest
5 голосов
/ 01 ноября 2011

Expression.Convert обычно добавляет InvalidOperationException, когда "Оператор преобразования не определен между выражением. Тип и тип."

Параметр типа возвращаемого значения Func<> является ковариантным для ссылочных типов.

// This works.
Func<SomeType> a = () => new SomeType();
Func<object> b = a;

Это не является ковариантным для типов значений .

Дисперсия применяется только к ссылочным типам; если вы указываете тип значения для параметра типа варианта, этот параметр типа является инвариантным для результирующего составного типа.

// This doesn't work!
Func<int> five = () => 5;
Func<object> fiveCovariant = five;

Однако Expression.Convert считает, что это возможно.

Func<int> answer = () => 42;
Expression answerExpression = Expression.Constant( answer );
// No InvalidOperationException is thrown at this line.
Expression converted 
    = Expression.Convert( answerExpression, typeof( Func<object> ) );

Нет InvalidOperationException выбрасывается при вызове Expression.Convert. Дерево выражений компилируется правильно, но когда я вызываю созданный делегат, я получаю ожидаемое InvalidCastException.

  1. Это ошибка? ( Я сообщил об ошибке в Microsoft Connect .)
  2. Как правильно проверить, можно ли преобразовать тип в другой тип? Некоторые ответы , кажется, относятся к использованию Convert. Я бы очень предпочел метод, который не должен использовать обработку исключений в качестве логики.

Кажется, вся логика отклонений не поддерживается должным образом. Он правильно жалуется на невозможность конвертировать из Func<SomeType> в Func<SomeOtherType>, но не жалуется на конвертацию из Func<object> в Func<string>.

Интересно, что если SomeType и SomeOtherType находятся в одной и той же иерархии классов (SomeOtherType расширяется от SomeType), исключение не выдается. Если нет, это так.

Ответы [ 3 ]

7 голосов
/ 01 ноября 2011

Это ошибка?

Да. Библиотека дерева выражений, вероятно, постоянно не обновлялась, когда мы добавляли ковариацию и контравариантность. Извините за это.

Я сообщил об этом как об ошибке в Microsoft Connect.

Спасибо! Тогда кто-нибудь взглянет на это.

Как правильно проверить, можно ли преобразовать тип в другой тип?

Вопрос неопределенный. Учитывая два типа объектов, вы хотите знать:

  • Думает ли среда выполнения .NET типы, совместимые с присваиванием?
  • Считает ли компилятор C #, что между типами существует неявное преобразование?
  • Считает ли компилятор C #, что между типами существует явное преобразование?

"int" и "short", например, не совместимы по назначению по правилам .NET. Int является явно конвертируемым, но не неявно конвертируемым в short, а short является неявно и явно конвертируемым в int по правилам C #.

1 голос
/ 01 ноября 2011

Это не ошибка.Expression.Convert представляет проверку типа во время выполнения, поэтому ожидаемое поведение будет вызывать InvalidCastException во время выполнения.

Редактировать: это не совсем правильно.Он точно не представляет проверку типа во время выполнения (Вот документация: http://msdn.microsoft.com/en-us/library/bb292051.aspx). Однако дерево выражений создается во время выполнения, поэтому тогда должна выполняться вся проверка типов.

Редактировать: Я тоже использую .NET 4.0.

Кстати, Convert не жалуется на преобразование из Func<object> в Func<string> , потому что это преобразование иногда допустимо. Это допустимо, если Func<object> является ковариантной ссылкой на объект, тип времени выполнения которого Func<string>. Пример:

Func<string> sFunc = () => "S";
Func<object> oFunc = sFunc;
Func<string> anotherSFunc = (Func<string>)oFunc;

Теперь, Convert решает, генерировать ли исключение InvalidOperationException, проверяя, является лиодин тип может быть приведен к другому.При проверке делегатов на предмет возможного преобразования ссылок, похоже, что код проверяет контравариантные (аргументные) параметры и выдает InvalidOperationException, если любой является типом значения.проверьте ковариантный (возвращаемый тип) параметр. Поэтому я начинаю подозревать, что это является ошибкой, хотя я склонен зарезервировать суждение об этом, пока не получушанс взглянуть на спецификацию (см. Эрика Липперта * Может быть, во вселенной что-то не так, но, вероятно, нет ), что у меня сейчас нет времени.

0 голосов
/ 05 ноября 2011

Эрик Липперт ответил на часть 1 моего вопроса: похоже, это ошибка. Я начал искать решение вопроса 2:

Как правильно проверить, можно ли преобразовать тип в другой тип?

Я просто фиксирую первую попытку Type.CanConvertTo( Type to ) метода в моей библиотеке . (Источники слишком сложны, чтобы публиковать здесь, извините.)

if ( fromType.CanConvertTo( toType ) )
{
    convertedExpression = Expression.Convert( expression, toType );
}

Пока что поддерживается проверка неявных преобразований для:

  • Простые неуниверсальные типы.
  • Дисперсия для вложенных универсальных интерфейсов и делегатов.
  • Инвариантность для параметров типа общего значения.

Не поддерживает:

  • Типовые ограничения.
  • Пользовательские операторы неявного преобразования.

Проходит все мои тесты на неявные преобразования . Хотя вы также можете указать явное преобразование (и я на самом деле использую его так же, как сейчас ), мне все еще нужно написать модульные тесты для всех этих сценариев.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...