Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrong overload giving compiler error

Using VS2013, in the following example two different errors are given when attempting to pass a function to a worker's constructor, yet, lambda functions with the same prototype are ok.

What am I doing wrong, and how can I change the definition of the GetA function to allow it to be passed?

In order to avoid confusion with similar-sounding questions caused by misunderstanding how class inheritance works, I've intentionally avoided any inheritance in this example.

WorkerA can only accept a Func<A> in it's contructor. WorkerAorB is more flexible, and can accept either a Func<A> or a Func<B>. WorkerAandB is the most capable, and can accept a Func<A> and a Func<B> simultaneously (or either). It is the closest to my real code.

However, when code in the manager attempts to instantiate workers, WorkerA works as expected, but WorkerAorB gives the error:

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

WorkerAandB gives

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

In each case, it seems that the compiler becomes unable to determine which overload to use when it is being passed a reference to a real function rather than a lambda or an existing Func<A> variable, and in the WorkerAandB case it is unambiguously selecting the WRONG overload, and giving an error about the return type of the passed function.

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!

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?

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.

like image 497
Steve Avatar asked Apr 17 '15 17:04

Steve


2 Answers

The key part of Eric Lippert's answer here, as it applies to this question, appears to be that "overhead resolution does not consider return types." So if you rewrite your example by replacing every Func with an Action, the errors disappear, as now there are non-empty argument lists by which the ambiguity is resolved.

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
    }
}
like image 104
Joe Farrell Avatar answered Nov 06 '22 04:11

Joe Farrell


The answer would be this :

    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
    }

As for why it didn't work without the casts.... Seems that for the compiler GetA is not from the type Func<A> but only a method group

like image 1
Sidewinder94 Avatar answered Nov 06 '22 05:11

Sidewinder94