Привести делегата к Func в C # - PullRequest
39 голосов
/ 15 декабря 2009

У меня есть код:

public delegate int SomeDelegate(int p);

public static int Inc(int p) {
    return p + 1;
}

Я могу разыграть Inc до SomeDelegate или Func<int, int>:

SomeDelegate a = Inc;
Func<int, int> b = Inc;

но я не могу разыграть Inc до SomeDelegate и после этого разыграть Func<int, int> обычным способом, подобным этому:

Func<int, int> c = (Func<int, int>)a; // Сompilation error

Как я могу это сделать?

Ответы [ 8 ]

60 голосов
/ 15 декабря 2009

Существует гораздо более простой способ сделать это, но все остальные ответы пропущены:

Func<int, int> c = a.Invoke; 

Подробнее см. в этом блоге .

45 голосов
/ 15 декабря 2009
SomeDelegate a = Inc;
Func<int, int> b = Inc;

это сокращение от

SomeDelegate a = new SomeDelegate(Inc); // no cast here
Func<int, int> b = new Func<int, int>(Inc);

Вы не можете привести экземпляр SomeDelegate к Func по той же причине, по которой вы не можете преобразовать строку в Dictionary - это разные типы.

Это работает:

Func<int, int> c = x => a(x);

который является синтаксическим сахаром для

class MyLambda
{
   SomeDelegate a;
   public MyLambda(SomeDelegate a) { this.a = a; }
   public int Invoke(int x) { return this.a(x); }
}

Func<int, int> c = new Func<int, int>(new MyLambda(a).Invoke);
24 голосов
/ 15 декабря 2009

Попробуйте это:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                                                           b.Target,
                                                           b.Method);
8 голосов
/ 15 декабря 2009

Проблема в том, что:

SomeDelegate a = Inc;

На самом деле не актерский состав. Это краткая форма:

SomeDelegate a = new SomeDelegate(Inc);

Поэтому здесь нет актеров. Простое решение вашей проблемы может быть таким (в C # 3.0)

Func<int,int> f = i=>a(i);
7 голосов
/ 15 февраля 2012

Это работает (по крайней мере, в C # 4.0 - не пробовал в более ранних версиях):

SomeDelegate a = Inc;
Func<int, int> c = new Func<int, int>(a);

Если вы посмотрите на IL, это скомпилирует точно такой же код, что и ответ Уинстона. Вот IL для второй строки из того, что я только что написал:

ldloc.0
ldftn      instance int32 ConsoleApplication1.Program/SomeDelegate::Invoke(int32)
newobj     instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object, native int)

И это именно то, что вы видите, если назначить a.Invoke в c.

Между прочим, хотя решение Диего является более эффективным, в результате получающийся делегат ссылается непосредственно на базовый метод, а не проходит через другой делегат, он не обрабатывает многоадресные делегаты правильно. Решение Уинстона делает, потому что оно просто полностью подчиняется другому делегату. Если вам нужно прямое решение, которое также обрабатывает делегатов с несколькими целями, вам нужно нечто более сложное:

public static TResult DuplicateDelegateAs<TResult>(MulticastDelegate source)
{
    Delegate result = null;
    foreach (Delegate sourceItem in source.GetInvocationList())
    {
        var copy = Delegate.CreateDelegate(
            typeof(TResult), sourceItem.Target, sourceItem.Method);
        result = Delegate.Combine(result, copy);
    }

    return (TResult) (object) result;
}

Кстати, это правильно для делегатов с единственной целью - в итоге будет получен только один делегат целевого типа, который напрямую ссылается на любой метод (и, где это применимо, объект), на который ссылался входной делегат.

4 голосов
/ 03 апреля 2012

Вы можете взломать приведение, используя трюк, в котором вы используете c # эквивалент объединения c ++. Сложной частью является структура с двумя членами, которые имеют [FieldOffset (0)]:

[TestFixture]
public class Demo
{
    public void print(int i)
    {
        Console.WriteLine("Int: "+i);
    }

    private delegate void mydelegate(int i);

    [StructLayout(LayoutKind.Explicit)]
    struct funky
    {
        [FieldOffset(0)]
        public mydelegate a;
        [FieldOffset(0)]
        public System.Action<int> b;
    }

    [Test]
    public void delegatetest()
    {
        System.Action<int> f = print;
        funky myfunky;
        myfunky.a = null;
        myfunky.b = f;

        mydelegate a = myfunky.a;

        a(5);
    }
}
4 голосов
/ 15 декабря 2009

Это такая же проблема, как эта:

public delegate int SomeDelegate1(int p);
public delegate int SomeDelegate2(int p);
...
  SomeDelegate1 a = new SomeDelegate1(Inc);
  SomeDelegate2 b = (SomeDelegate2)a;  // CS0030

, что является такой же проблемой, как:

public class A { int prop { get; set; } }
public class B { int prop { get; set; } }
...
  A obja = new A();
  B objb = (B)obja;  // CS0029

Объекты не могут быть преобразованы из одного типа в несвязанный другой тип, даже если эти типы полностью совместимы. Из-за отсутствия лучшего термина: объект имеет идентичность типа, которую он переносит во время выполнения. Эта личность не может быть изменена после создания объекта. Видимым проявлением этой идентичности является Object.GetType ().

0 голосов
/ 30 июля 2018

Мне нравятся примеры. Вот мой пример кода:

class Program
{
    class A
    {
        public A(D d) { d.Invoke("I'm A!"); }
        public delegate string D(string s);
    }

    class B
    {
        public delegate string D(string s);
    }
    static void Main(string[] args)
    {
        //1. Func to delegates 

        string F(dynamic s) { Console.WriteLine(s); return s; }
        Func<string, string> f = F;
        //new A(f);//Error CS1503  Argument 1: cannot convert from 'System.Func<string, string>' to 'ConsoleApp3.Program.A.D'  
        new A(new A.D(f));//I'm A!
        new A(x=>f(x));//I'm A!

        Func<string, string> f2 = s => { Console.WriteLine(s); return s; };
        //new A(f2);//Same as A(f)
        new A(new A.D(f2));//I'm A!
        new A(x => f2(x));//I'm A!

        //You can even convert between delegate types
        new A(new A.D(new B.D(f)));//I'm A!



        //2. delegate to F

        A.D d = s => { Console.WriteLine(s); return s; };

        Func<string, string> f3 = d.Invoke;
        f3("I'm f3!");//I'm f3!
        Func<string, string> f4 = new Func<string, string>(d);
        f4("I'm f4!");//I'm f4!


        Console.ReadLine();
    }
}

Вывод:

enter image description here

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