在C#中将委托转换为Func

And*_*hin 43 .net c# delegates casting

我有代码:

public delegate int SomeDelegate(int p);

public static int Inc(int p) {
    return p + 1;
}
Run Code Online (Sandbox Code Playgroud)

我可以投IncSomeDelegateFunc<int, int>:

SomeDelegate a = Inc;
Func<int, int> b = Inc;
Run Code Online (Sandbox Code Playgroud)

但我不能投IncSomeDelegate和投后Func<int, int>用这样的常用方法:

Func<int, int> c = (Func<int, int>)a; // ?ompilation error
Run Code Online (Sandbox Code Playgroud)

我怎么能这样做?

Win*_*ith 67

有一种更简单的方法可以做到这一点,所有其他答案都错过了:

Func<int, int> c = a.Invoke; 
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅此博客文章.

  • 如果有人不确定迭戈的含义,请查看原始'a'委托的目标和方法属性以及'c'委托.使用迭代的机制,'c'直接指向原始方法,就像'a'一样.使用Winston的方法,它没有 - 它指向代理,而代理又指向原始方法,因此您获得了不必要的额外级别的间接. (7认同)
  • 尼斯.它与Gamlor的解决方案类似,但没有匿名方法.不过,与我提出的解决方案不同,它正在包装原始代表. (3认同)
  • 正如迭戈和伊恩所提到的,这个解决方案将原始代表包装起来.因此,如果您将此解决方案与事件一起使用,则无法取消订阅.有了Diego的解决方案,你可以. (2认同)
  • @Verax @IanGriffiths:我做了一个快速实验(net45)和`a.Invoke`似乎取消订阅好吗?https://gist.github.com/dtchepak/7799703 (2认同)

dtb*_*dtb 48

SomeDelegate a = Inc;
Func<int, int> b = Inc;
Run Code Online (Sandbox Code Playgroud)

是的缩写

SomeDelegate a = new SomeDelegate(Inc); // no cast here
Func<int, int> b = new Func<int, int>(Inc);
Run Code Online (Sandbox Code Playgroud)

您无法将SomeDelegate的实例强制转换为Func <int,int>,原因与您无法将字符串强制转换为Dictionary <int,int>相同 - 它们是不同的类型.

这有效:

Func<int, int> c = x => a(x);
Run Code Online (Sandbox Code Playgroud)

这是语法糖

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);
Run Code Online (Sandbox Code Playgroud)

  • 也许强调您编写的自定义内容通常由编译器魔术完成的事实。 (3认同)
  • +1 很好的解释。有一种比 *Func&lt;int, int&gt; c = x =&gt; a(x);* 更简单的方法 - 请参阅我的答案。 (3认同)

Die*_*hon 29

试试这个:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                                                           b.Target,
                                                           b.Method);
Run Code Online (Sandbox Code Playgroud)

  • 你是对的,它主要用于`void`返回类型,就像`EventHandler`代表有许多订阅者.但试试这个:`Func <int> a =()=> 10; Func <int> b =()=> 20; Func <int> c =()=> 30; var multi = a + b + c; int answer = multi();`它在语言和框架中是合法的,所以你必须确保没有人使用它. (2认同)
  • 我看到 [没有性能提升](https://gist.github.com/RobertBouillon/ec73769e338b8ccbef8f4865c855cb15/edit) 在 .NET Core 2.1 和 Framework 4.7.2 中使用这种方法。虚拟呼叫在两者中都以**非常**小的幅度变慢(正如一些人预测的那样)。如果您需要这种级别的优化,最好将精力投入到较低级别的语言上,例如 C++。即使您设法以这种方式优化 .NET,也不能保证它适用于所有平台或未来版本。 (2认同)

Gam*_*lor 9

问题是:

SomeDelegate a = Inc;
Run Code Online (Sandbox Code Playgroud)

实际上不是演员.这是简短的形式:

SomeDelegate a = new SomeDelegate(Inc);
Run Code Online (Sandbox Code Playgroud)

因此,没有演员.一个简单的问题解决方案可以是这个(在C#3.0中)

Func<int,int> f = i=>a(i);
Run Code Online (Sandbox Code Playgroud)

  • 是啊,你说得对.优雅代码与性能.取决于你需要你选择的东西. (2认同)

Ian*_*ths 8

这是有效的(至少在C#4.0中 - 在早期版本中未尝试过):

SomeDelegate a = Inc;
Func<int, int> c = new Func<int, int>(a);
Run Code Online (Sandbox Code Playgroud)

如果你看一下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)
Run Code Online (Sandbox Code Playgroud)

而这也是你看到的正是如果分配a.Invokec.

顺便提一下,虽然Diego的解决方案更有效,但是生成的委托直接引用底层方法而不是通过另一个委托,它不能正确处理多播委托.温斯顿的解决方案确实如此,因为它完全推迟到另一个代表.如果您想要一个直接解决方案来处理具有多个目标的委托,您需要一些更复杂的东西:

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;
}
Run Code Online (Sandbox Code Playgroud)

对于具有单个目标的委托来说,这是正确的事情 - 它最终将只生成目标类型的单个委托,该委托直接引用输入委托所引用的任何方法(以及适用的对象).


Luc*_*jer 5

您可以通过技巧来破解演员表,在技巧中使用与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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 第一次,我听说过这种异常危险的黑客行为。谢谢! (3认同)