将方法组作为委托

时间:2015-08-13 17:29:13

标签: c#

希望有人可以向我解释。对不起,如果重复一遍,那么解释我所看到的内容的关键词现在已超出我的范围了。

这里有一些编译

的代码
class Program
{
    static void Main(string[] args)
    {
        new Transformer<double, double>(Math.Sqrt);
    }
}

class Transformer<Tin, Tout>
{
    Func<Tin, Task<Tout>> actor;
    public Transformer(Func<Tin, Tout> actor)
    {
        this.actor = input => Task.Run<Tout>(() => actor(input));
    }
}

这里有一些不是

的代码
class Program
{
    static void Main(string[] args)
    {
        new Transformer<double, double>(Math.Sqrt);
    }
}

public class Transformer<Tin, Tout>
{
    Func<Tin, Task<Tout>> actor;
    public Transformer(Func<Tin, Tout> actor)
    {
        this.actor = input => Task.Run<Tout>(() => actor(input));
    }

    public Transformer(Func<Tin, Task<Tout>> actor)
    {
        this.actor = actor;
    }
}

通过添加构造函数重载,这显然会产生歧义,但我不确定原因。 Math.Sqrt没有重载,显然返回类型为double,而不是Task&lt; double&gt ;.

这是错误:

  

以下方法或属性之间的调用不明确:&#39; ConsoleApplication1.Transformer&lt; double,double&gt; .Transformer(System.Func&lt; double,double&gt;)&#39;和&#39; ConsoleApplication1.Transformer&lt; double,double&gt; .Transformer(System.Func&lt; double,System.Threading.Tasks.Task&lt; double&gt;&gt;)&#39;

有人可以解释为什么选择对编译器来说不明显吗?

为那些关心的人提供简单的解决方法:

class Program
{
    static void Main(string[] args)
    {
        new Transformer<double, double>(d => Math.Sqrt(d));
    }
}

1 个答案:

答案 0 :(得分:5)

您对Func<Tin, Tout>的工作方式略有误解。看看the docs

public delegate TResult Func<in T, out TResult>(
    T arg
)

第一个参数是参数,最后一个参数是返回类型。

当您查看代码的简化版本时:

internal class Program
{
    public static void Main(string[] args)
    {
        new MyClass<double, double>(Method);
    }

    private static double Method(double d)
    {
        throw new NotImplementedException();
    }
}


internal class MyClass<T, U>
{
    public MyClass(Func<U, T> arg)
    {
    }

    public MyClass(Func<U, Task<T>> arg)
    {
    }
}

您会注意到两个参数首先指定double,这是一个参数,然后返回类型不同:T vs Task<T>

但是我们都知道:重载是基于方法名称,参数arity和参数类型完成的。返回类型完全被忽略。在我们的情况下,这意味着我们有两个Func<Tin, Tout>double作为参数,TTask<T>作为返回类型。

切换参数编译就好了:

internal class MyClass<T, U>
{
    public MyClass(Func<T, U> arg)
    {
    }

    public MyClass(Func<Task<T>, U> arg)
    {
    }
}

如果您在Visual Studio中查看,您会注意到此方法现在变为灰色,这是有道理的,因为Method的参数属于double类型,因此将始终与T匹配,而不是Task<T>

因此,为了测试它是否会在传递不同的异步方法时遇到正确的重载,您可以添加第二种方法:

private static double MethodAsync(Task<double> d)
{
   throw new NotImplementedException();
}

并使用

调用它
new MyClass<double, double>(MethodAsync);

现在您将注意到异步Func<Task<T>, U>>已被命中(您可以通过从构造函数中简单地打印到控制台来验证)。

简而言之:您尝试在返回类型上执行重载解析,这显然是不可能的。