【问题标题】:Why can't I implicitly cast a Delegate with Extension methods?为什么我不能使用扩展方法隐式转换委托?
【发布时间】:2010-10-07 07:58:03
【问题描述】:

我正在尝试找出一种自动将某些内容投射到 Action 或 Func 的方法,而我能想到的最好的方法是这样的:

[TestFixture]
public class ExecutionTest
{
    public void BadMethod()
    {
        throw new Exception("Something bad happened");
    }

    [Test]
    public void TestBadMethod()
    {
        // Want this, but it won't work!!
        // BadMethod.Execute().IgnoreExceptions();

        // Ick
        ((Action)BadMethod).Exec().IgnoreExceptions();

        // Still ick
        ((Action)BadMethod).IgnoreExceptions();

        // Do not want
        ExtensionMethods.Exec(BadMethod).IgnoreExceptions();

        // Better but still meh
        this.Exec(BadMethod).IgnoreExceptions();

    }
}

public static class ExtensionMethods
{
    public static Action Exec(this Action action)
    { return action; }

    public static Action Exec(this object obj, Action action)
    { return action; }

    public static void IgnoreExceptions(this Action action)
    {
        try { action(); }
        catch {}
    }
}

必须有更好/更简单的方法来做到这一点,有什么想法吗?

【问题讨论】:

    标签: c# functional-programming extension-methods currying


    【解决方案1】:

    在 C# 中,当您使用不带括号的方法名称时,它被称为方法组,它除了在编译时没有任何表示。一个方法组可以表示多个方法(因为重载和覆盖),因此要隐式识别需要哪个方法,必须提供目标委托类型。

    在您的情况下,您想知道为什么扩展方法参数类型不会触发函数的解析。简单来说,extension是在类型已知后才计算的,也就是说this参数不能作为隐式转换目标。

    为什么会中断的示例:

    class Test
    {
        void M (void) // Fits Action delegate
        {
        }
    
        int M (int) // Fits Func<int,int> delegate
        {
            return 5;
        }
    
        void Test()
        {
            M.Exec(); // UHOH!!! Which Exec to resolve to ???
        }
    }
    
    
    public static class Extensions
    {
        public static void Exec(this Action action) { }
        public static void Exec(this Func<int, int> func) { }
    }
    

    如您所见,存在冲突,但事实上,冲突从未发生过,因为 C# 甚至不会尝试找到与方法组匹配的扩展。

    请注意这也不行:

    class A
    {
        public static implicit operator int (A a)
        {
            return 5;
        }
    
        void F()
        {
           A a = new A();
           a.Blah(); // Error! It won't implicitly try C.Blah()
        }
    }
    
    public static class C
    {
        public static void Blah (int i)
        {
        }
    }
    

    C# 不会将 A 匹配到 C.Blah(int),因为它需要隐式转换。

    【讨论】:

    • 不确定这是方法重载的问题,因为编译器会找出调用哪个方法。但这是迄今为止我见过的最好的答案......
    【解决方案2】:

    正如 Coincoin 所说,由于对方法重载的过分热爱,它在 C# 中不会很好地工作。我见过人们使用的唯一解决方法是创建 Action 和 Func 方法:

    public Action Action(Action f) { return f; }
    public Action<A> Action<A>(Action<A> f) { return f; }
    ...
    public Func<A,B,C,D,E> Func(Func<A,B,C,D,E> f) { return f; }
    

    您甚至可以将它们都称为“F”以获得某种简短的语法:

    F(BadMethod).NoExceptions();
    

    您可能决定不在您的类中定义这些方法,而是将它们放在 Funcs 实用程序或其他东西中。用 F 给它起别名,结果还不错:

    F.F(BadMethod).NoException();
    

    但总的来说还是很烂:(。

    【讨论】:

    • 是的,这就是为什么我有(this Object object,Action action)的扩展方法,所以它可以通过调用 this.Action() 或 this.Func() 在任何类中工作
    • 对不起,我没有看到 Exec 的定义——有点误导的名字,没有:P
    • 是的,我在我的实际代码中重命名了它。当时看起来很“流利”。
    【解决方案3】:

    F# 通过提供更好的类型推断系统,让您可以非常自然地做这种事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-07
      相关资源
      最近更新 更多