Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No extension method 'First' on derived class

Given the (very simplied) code.

public class Class1 
{
}

public class Class2 : Class1
{
}

public class List1 : System.Collections.Generic.IEnumerable<Class1>
{
    public new System.Collections.Generic.IEnumerator<Class1> GetEnumerator()
    {
        yield return new Class1();            
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}


public class List2  : List1 , System.Collections.Generic.IEnumerable<Class2> 
{       
    public new System.Collections.Generic.IEnumerator<Class2> GetEnumerator()
    {
        yield return new Class2();              
    }  

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Then the code

var l = new List2();
var first = l.First();

will not compile, but gives the error

'List2' does not contain a definition for 'First' and no extension method 'First' accepting a first argument of type 'List2' could be found (are you missing a using directive or an assembly reference?)

If List2 is not derived from List1 then it compiles ok, which proves that List2 does have a valid extension method.

Is this simply a case of an misleading error and the problem is it has two many extension methods and doesn't know which one to pick?

If so, why can't it tell that Class2 is more specific version in the same way as the compiler uses with method overload resolution?

like image 989
sgmoore Avatar asked May 29 '13 16:05

sgmoore


2 Answers

The problem is that List2 implements both IEnumerable<Class1> and IEnumerable<Class2>, so the compiler doesn't know which of Enumerable.First<Class1> or Enumerable.First<Class2> to call. Effectively, there is no best T that the compiler can find to invoke Enumerable.First<T>(l), and so it does not count any generic Enumerable.First<T> as an eligible method. Therefore, it's now looking only for a non-generic method named First with a single parameter that l can be implicitly converted to, and it can't find any.

You can be explicit and say

var first = l.First<Class1>();

or

var first = l.First<Class2>();

and then you're okay.

like image 126
jason Avatar answered Oct 03 '22 15:10

jason


Consider this class :

public class List1 : IEnumerable<string>, IEnumerable<object>
        {
            IEnumerator<object> IEnumerable<object>.GetEnumerator()
            {
                return GetEnumerator();
            }

            public IEnumerator<string> GetEnumerator()
            {
                throw new NotImplementedException();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }

so compiler does not know First() method of which of IEnumerable<string> or IEnumerable<object> to call. this example is simplified version of your situation.

Consider you have written an extension method on a class that you don't have its source code, but then in next version they implement exactly the same method on that class. In this situation your old codes are not reliable anymore, so the best solution is a compile time error.

like image 45
VahiD Avatar answered Oct 03 '22 16:10

VahiD