错误的重载导致编译器错误

Wrong overload giving compiler error

使用 VS2013,在下面的示例中,当尝试将函数传递给 worker 的构造函数时会给出两个不同的错误,但是具有相同原型的 lambda 函数是可以的。

我做错了什么,如何更改 GetA 函数的定义以允许它通过?

为了避免因误解 class 继承的工作原理而与听起来相似的问题混淆,我在这个例子中有意避免了任何继承。

WorkerA 只能在其构造函数中接受 Func<A>WorkerAorB 更灵活,可以接受 Func<A>Func<B>WorkerAandB 是最有能力的,可以同时(或其中之一)接受 Func<A>Func<B>。最接近我的真实代码。

但是,当管理器中的代码尝试实例化 worker 时,WorkerA 按预期工作,但 WorkerAorB 给出错误:

error CS0121: The call is ambiguous between the following methods or properties: 'WorkerAorB.WorkerAorB(System.Func<A>)' and 'WorkerAorB.WorkerAorB(System.Func<B>)'

WorkerAandB 给出

error CS0407: 'A ManagerA.GetA()' has the wrong return type

在每种情况下,似乎编译器都无法确定在将引用传递给实际函数而不是 lambda 或现有 Func<A> 变量时使用哪个重载,并且在WorkerAandB 情况下,它明确选择了错误的重载,并给出了有关传递函数的 return 类型的错误。

class A { }
class B { }

class WorkerA
{
  public WorkerA(Func<A> funcA) { }
}

class WorkerAorB
{
  public WorkerAorB(Func<A> funcA) { }
  public WorkerAorB(Func<B> funcB) { }
}

class WorkerAandB
{
  public WorkerAandB(Func<A> funcA, Func<B> funcB = null) { }
  public WorkerAandB(Func<B> funcB) { }
}
class ManagerA
{
  A GetA() { return new A(); }
  static A GetAstatic() { return new A(); }
  Func<A> GetAfunc = GetAstatic;

  ManagerA()
  {
    new WorkerA(() => new A()); // ok
    new WorkerA(GetA); // ok
    new WorkerA(GetAstatic); // ok

    new WorkerAorB(() => new A()); // ok
    new WorkerAorB(() => new B()); // ok
    new WorkerAorB(GetA); // error CS0121
    new WorkerAorB(GetAstatic); // error CS0121
    new WorkerAorB(() => GetA()); // ok
    new WorkerAorB(GetAfunc); // ok

    new WorkerAandB(() => new A()); // ok
    new WorkerAandB(GetA); // error CS0407
    new WorkerAandB(GetAstatic); // error CS0407
    new WorkerAandB(GetA, null); // ok
    new WorkerAandB(GetAstatic, null); // ok
    new WorkerAandB(GetAfunc); // ok
  }
}

// class ManagerB or ManagerAandB left as an exercise to the reader!

能否以某种方式修改 GetAGetAstatic 函数以帮助编译器识别要使用的正确重载,或者只允许 lambdas and/or 明确声明的委托这个语境?

Update: Some information that I'd omitted from the example. In the real problem, classes A and B are in fact related.

class B : A { }

Also, on further reflection of the real problem, a call to

public WorkerAandB(Func<B> funcB) { }

like

    new WorkerAandB(GetB)

was in fact equivalent to

    new WorkerAandB(GetB, GetB)

So for the real problem, I've done the equivalent of deleting the second constructor in the example problem, as it turns out the overload was redundant.

In the meantime I've accepted the answer that actually gave a potential solution to the problem (albeit an obvious one that I'd omitted to mention in the original question), even though it wasn't what I eventually used.

答案是这样的:

    public ManagerA()
    {
        new WorkerA(() => new A()); // ok
        new WorkerA(GetA); // ok
        new WorkerA(GetAstatic); // ok

        new WorkerAorB(() => new A()); // ok
        new WorkerAorB(() => new B()); // ok
        new WorkerAorB((Func<A>)GetA); // cast to avoid error CS0121
        new WorkerAorB((Func<A>)GetAstatic); // cast to avoid error CS0121
        new WorkerAorB(() => GetA()); // ok
        new WorkerAorB(GetAfunc); // ok

        new WorkerAandB(() => new A()); // ok
        new WorkerAandB((Func<A>)GetA); // cast to avoid error CS0407
        new WorkerAandB((Func<A>)GetAstatic); // cast to avoid error CS0407
        new WorkerAandB(GetA, null); // ok
        new WorkerAandB(GetAstatic, null); // ok
        new WorkerAandB(GetAfunc); // ok
    }

至于为什么没有强制转换它就不能工作....似乎对于编译器 GetA 不是来自类型 Func<A> 而只是 method group

总的问题是你为什么要写这样的代码?我看到了问题的假设性质,但它不应该存在于现实世界中,因为我们应该编写实际按预期读取的函数:

public Person GetPersonById(func<int> personIdFunc)

没有其他方法可以通过传入 returns 一个整数的函数来编写一个函数来通过 ID 获取一个人。

或者在创建多个构造函数时,使用正确的面向对象方法解决了一个问题:

class Person
{
  public Person(Func<B> funcB) 
    :this(null, funcB)
  { }
  public Person(Func<A> funcA, Func<B> funcB) { }
}

但是直接针对你的问题..

Can the GetA or GetAstatic functions be modified in some way to help the compiler recognise the correct overload to use, or are only lambdas and/or explicitly-declared delegates allowed in this context?

据我所知,它不能在函数定义上,但可以在它的使用上完成:

new WorkerAorB((Func<A>)GetA); // error CS0121
new WorkerAorB((Func<A>)GetAstatic); // error CS0121
new WorkerAandB((Func<A>)GetA); // error CS0407
new WorkerAandB((Func<A>)GetAstatic); // error CS0407

Eric Lippert 的回答 here 的关键部分,因为它适用于这个问题,似乎是 "overhead resolution does not consider return types." 因此,如果您通过将每个 Func 替换为Action,错误消失了,因为现在有非空的参数列表来解决歧义。

class A { }
class B { }

class WorkerA
{
    public WorkerA(Action<A> doA) { }
}

class WorkerAorB
{
    public WorkerAorB(Action<A> doA) { }
    public WorkerAorB(Action<B> doB) { }
}

class WorkerAandB
{
    public WorkerAandB(Action<A> doA, Action<B> doB = null) { }
    public WorkerAandB(Action<B> doB) { }
}
class ManagerA
{
    void DoA(A a) { }
    static void DoAstatic(A a) { }
    Action<A> DoAfunc = DoAstatic;

    ManagerA()
    {
        new WorkerA((A a) => { }); // ok
        new WorkerA(DoA); // ok
        new WorkerA(DoAstatic); // ok

        new WorkerAorB((A a) => { }); // ok
        new WorkerAorB((B b) => { }); // ok
        new WorkerAorB(DoA); // ok
        new WorkerAorB(DoAstatic); // ok
        new WorkerAorB(a => { }); // ok
        new WorkerAorB(DoAfunc); // ok

        new WorkerAandB(a => { }); // ok
        new WorkerAandB(DoA); // ok
        new WorkerAandB(DoAstatic); // ok
        new WorkerAandB(DoA, null); // ok
        new WorkerAandB(DoAstatic, null); // ok
        new WorkerAandB(DoAfunc); // ok
    }
}