【问题标题】:Wrong overload giving compiler error错误的重载导致编译器错误
【发布时间】:2015-04-17 17:42:59
【问题描述】:

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

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

为了避免因误解类继承的工作原理而导致与听起来相似的问题混淆,我在此示例中有意避免了任何继承。

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

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

错误 CS0121:以下方法之间的调用不明确或 属性:'WorkerAorB.WorkerAorB(System.Func)' 和 'WorkerAorB.WorkerAorB(System.Func)'

WorkerAandB给了

错误 CS0407:“A ManagerA.GetA()”的返回类型错误

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

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 函数以帮助编译器识别要使用的正确重载,或者在此上下文中仅允许使用 lambda 和/或显式声明的委托?

更新:我在示例中省略了一些信息。在真实 问题,AB 类实际上是相关的。

class B : A { }

此外,在进一步反映实际问题时,调用

public WorkerAandB(Func<B> funcB) { }

喜欢

    new WorkerAandB(GetB)

实际上等价于

    new WorkerAandB(GetB, GetB)

所以对于真正的问题,我已经做了相当于删除 示例问题中的第二个构造函数,因为事实证明重载是多余的。

与此同时,我已经接受了实际上具有潜力的答案 问题的解决方案(尽管我忽略了一个明显的问题 在最初的问题中),即使它不是我最终使用的。

【问题讨论】:

  • 让我们希望@EricLippert 会出现,或者有C#规范的人出现:)
  • 可能与 Eric 和 Jon 的答案重复 stackoverflow.com/Questions/2057146/…
  • 我当然希望有人 casts 对这个问题有所了解

标签: c# lambda overload-resolution member-functions


【解决方案1】:

Eric Lippert 的回答 here 的关键部分,因为它适用于这个问题,似乎是“开销解析不考虑返回类型”。因此,如果您通过将每个 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
    }
}

【讨论】:

  • 从 Func 切换到 Action 可以解决编译错误 - 但它会改变程序的语义/行为。 IE。它不能解决问题。
  • 当然你是对的@JimiLoe。我并不是说我的重写在功能上等同于原始代码。我发布它只是为了证明DoAFunc&lt;A&gt;Func&lt;B&gt; 都具有相同的参数列表,这解释了为什么方法组在有多个时不能隐式转换为委托类型可能的匹配。
【解决方案2】:

答案是这样的:

    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&lt;A&gt; 类型,而只是method group

【讨论】:

  • 这似乎是唯一真正解决问题的答案......而且我没有在问题中提到演员解决方案......事实上我最终使用了不同的解决方案,使用示例中不存在的实际问题的属性。
【解决方案3】:

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

public Person GetPersonById(func<int> personIdFunc)

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

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

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

但直接回答你的问题..

是否可以通过某种方式修改 GetA 或 GetAstatic 函数以帮助编译器识别要使用的正确重载,或者在此上下文中仅允许使用 lambda 和/或显式声明的委托?

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

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

【讨论】:

  • 在真实代码中,只有WorkerAandB等价物存在,Manager等价物(目前是单元测试,因为还没有真正的manager),只实例化一个worker。其他类用于说明有效与无效之间的界限。客户端代码可以提供返回 A 的委托、返回 B 的委托或两者都提供。如果缺少一个或另一个,“工人”可以使用效率较低的方法从 B 类生成 A 类,而 B 类使用重复调用返回类 A 的委托。
  • 另外,我会注意到,当添加 public Person(Func funcA) : this(funcA, null) 时,问题会重新出现在您的示例中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-17
  • 1970-01-01
  • 2015-06-29
  • 1970-01-01
相关资源
最近更新 更多