В C # 4 дисперсия будет работать с типами делегатов, которые созданы с совместимыми ссылочными типами. Однако он никогда не будет работать с делегатами, в которых аргументы типа являются типами значений.
Причина в том, что мы не можем сделать это без смещения стека. Рассмотрим ваш пример. У вас есть Func<int, int>
. Предположим, мы разрешили вам преобразовать его в Func<int, double>
. Первый снимает со стека 4 байта и снова помещает 4, вызывая изменение чистого стека на 0. Последний снимает 4 и кладет 8, вызывая изменение чистого стека на 4 байта. Вызывающая функция будет читать 8 байт из стека, но в стеке только 4, а остальное - мусор; в результате получится мусор, стек будет смещен, а среда выполнения в конечном итоге ужасно рухнет.
Теперь мы могли бы выполнить «преобразование», выделив новый метод, который исправляет его - он принимает 4-байтовое int и превращает его в 8-байтовый дубль. Но это приводит к неожиданным последствиям. Когда вы говорите
Exception x = new Exception();
object y = x;
вы ожидаете, что будет ОДИН объект - вы не ожидаете, что преобразование x в объект приводит к выделению памяти, помещая оболочку вокруг объекта. Вы ожидаете, что "x == y" будет истинно для сравнения ссылок. Если бы мы автоматически делали исправления для преобразований между несовместимыми типами делегатов, тогда преобразование (1) выделило бы память и (2) разрушило бы равенство ссылок. Оба противны всей идее конверсионных ссылок; вся точка эталонных преобразований заключается в том, что они дешевы, быстры и сохраняют референтную идентичность .
Если вы хотите выделить нового делегата, который фиксирует возвращаемое значение существующего делегата, вы можете это сделать; то, что мы делаем это явным в коде, чтобы было ясно, что ссылочная идентичность уничтожается, - это хорошо.