Проблемы приведения объектов типа Func <T, T> в C # - PullRequest
2 голосов
/ 09 января 2010

Почему это не компилируется? Я полагаю, что есть способы обойти это; просто любопытно.

Спасибо!

    static void Main(string[] args)
    {
        Func<int, int> f_int = n => n * n;
        Func<int, double> f_double = (Func<int, double>)f_int;
    }

Ответы [ 3 ]

7 голосов
/ 10 января 2010

В 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) разрушило бы равенство ссылок. Оба противны всей идее конверсионных ссылок; вся точка эталонных преобразований заключается в том, что они дешевы, быстры и сохраняют референтную идентичность .

Если вы хотите выделить нового делегата, который фиксирует возвращаемое значение существующего делегата, вы можете это сделать; то, что мы делаем это явным в коде, чтобы было ясно, что ссылочная идентичность уничтожается, - это хорошо.

6 голосов
/ 09 января 2010

Дженерики не имеют такого типа дисперсии; не сейчас и не (для типов значений, таких как int / double) в 4.0. Проще говоря, f_int не возвращает double. Лучшее, что вы можете сделать, это:

Func<int, double> f_double = i => f_int(i);

, который имеет неявное преобразование из int в double в возвращаемое значение.

1 голос
/ 09 января 2010

Delgates не могут быть конвертированы, так как double не наследуется от int или наоборот.

Временные решения просто используют double для сохранения результата в:

double r = f_int(a);

или использование dynamic в C # 4.0:

Func<dynamic, dynamic> f = n => n*n;
double d = f(2.0);
int i = f(2);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...