Подтвердить что ты не робот

Передача делегата Func в С#

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

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

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

4b9b3361

Ответ 1

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 < int, int > по той же причине вы не можете передать строку в словарь < int, int > - они разные.

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

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);

Ответ 2

Там гораздо более простой способ сделать это, о чем пропустили все остальные ответы:

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

Смотрите этот пост в блоге для получения дополнительной информации.

Ответ 3

Попробуйте следующее:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                                                           b.Target,
                                                           b.Method);

Ответ 4

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

SomeDelegate a = Inc;

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

SomeDelegate a = new SomeDelegate(Inc);

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

Func<int,int> f = i=>a(i);

Ответ 5

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

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

Если вы посмотрите на IL, это скомпилируется в тот же код, что и Winston. Здесь 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.

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

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;
}

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

Ответ 6

Это та же проблема:

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().

Ответ 7

Вы можете взломать бросок, используя трюк, в котором вы используете эквивалент С# для объединения С++. Трудная часть - это структура с двумя членами, которые имеют [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);
    }
}