为什么无法使用扩展方法隐式转换委托?

7
我正在尝试找到一种自动将某物转换为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 {}
    }
}

有没有更好/更简单的方法来做这件事?你有什么想法吗?

3个回答

5
在C#中,当您在不使用括号的情况下使用方法名称时,它被称为方法组,并且除了在编译时没有其他表示。方法组可以代表多个方法(因为存在重载和覆盖),因此要隐式地标识需要哪个方法,必须提供目标委托类型。
在您的情况下,您想知道为什么扩展方法参数类型不会触发函数的解析。简单地说,扩展是在已知类型之后评估的,也就是说,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# 不会将 AC.Blah(int) 匹配,因为这需要进行隐式转换。

我不确定这是否是方法重载的问题,因为编译器会找出应该调用哪一个。但这是目前为止我见过的最好的答案... - user43305

3

正如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() 在任何类中使用。 - user43305
抱歉,我没有看到Exec的定义 - 名称有点误导人,哈哈 :P - MichaelGG
是的,我在我的实际代码中将其重命名了。当时它看起来很“流畅”。 - user43305

1

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接