Примите во внимание следующее:
Func<Animal, Turtle> fat = whatever;
Func<Mammal, Reptile> fmr = fat;
Это законно. fat
может забрать любое животное, а вызывающий fmr
обещает передать Mammal
, что всегда равно Animal
. Точно так же fmr
требует, чтобы функция возвращала Reptile
, а fat
всегда возвращает Turtle
, что является Reptile
.
Этот вид преобразования называется вариантом конверсия. В частности, «Мне требуется любое животное, поэтому я приму млекопитающее», которое называется контравариантным преобразованием, а «Я возвращаю черепаху, чтобы удовлетворить вашу потребность в рептилии» - это ковариант * Преобразование 1020 *.
C# поддерживает ковариантные и контравариантные преобразования для обобщенных c типов только при следующих обстоятельствах:
- Типом, о котором идет речь, является делегат или интерфейс.
- Тип был помечен как безопасный для отклонения, и компилятор подтвердил, что он безопасен.
- Все аргументы типа, которые могут быть разными, являются ссылочными типами .
Вы удовлетворяете первым двум условиям - ваш Func<string, object>
является типом делегата, который, как известно, безопасен для контравариантности в аргументе и ковариантности в возвращаемом. Однако вы не соответствуете третьему условию. bool?
DateTime?
, double?
и int?
являются не ссылочными типами . Следовательно, правила преобразования вариантов не применяются, и преобразования являются незаконными.
Это будет включать преобразование из Func<string, bool?>
в Func<string, object>
. Но это не то, что вы делаете; вы выполняете преобразование из группы методов , содержащей функцию, которая возвращает bool?
. Это имеет значение?
Нет. Правила одинаковы для преобразований групп методов. Ковариантное преобразование из группы методов, содержащей метод, который возвращает bool?
в Func<string, object>
, недопустимо, поскольку bool?
равно все еще , необходимому для того, чтобы сделать этот тип ссылки допустимым.
Независимо от того, как вы его нарезаете, вы застряли. Вы не можете преобразовать метод или делегат, который возвращает bool?
напрямую , в делегат, который возвращает object
.
Причина этого хорошо изложена в комментарии Йероена к этому вопросу. Где преобразование в бокс go? Преобразование в бокс код, который выделяет кучу памяти , и, следовательно, этот код должен go где-то , но есть компилятору некуда его поместить и по-прежнему поддерживать ссылочный идентификатор делегата.
Как вы правильно заметили, если вы предоставляете свою собственную лямбду, то компилятору есть место для размещения преобразования в бокс; это прикрепляет это сверху возвращения от лямбды. Ценой, которую вы платите за это, является то, что лямбда теперь является делегатом, который ссылается на тело лямбды , а не прямо на GetBooleanValue
, так что здесь есть крошечный штраф за косвенное обращение.