co-logic
co-logic

Reputation: 65

Generic lists: The type arguments for method cannot be inferred from the usage in lambdas

I got a problem in C#, giving me an error 'The type arguments for method cannot be inferred from the usage'. Seems that the compiler cannot determine the correct interface, if I derive a generic list from a non-generic one:

Code:

public class SpecialItem : BaseItem
{
    public string Title { get; set; }
}

public class BaseItem
{
    public string Name { get; set; }
}


public class GenericList<T> : NongenericBaseList, IEnumerable<T>
    where T: BaseItem
{
    public new T this[int index]
    {
        get { return _items[index] as T; }
    }

    public new IEnumerator<T> GetEnumerator()
    {
        var iter = _items.GetEnumerator();
        while (iter.MoveNext())
        {
            yield return iter.Current as T;
        }
    }
}


public class NongenericBaseList : IEnumerable<BaseItem>
{
    protected List<BaseItem> _items;

    public BaseItem this[int index]
    {
        get { return _items[index]; }
    }

    public IEnumerator<BaseItem> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

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

Usage:

    var genericList = new GenericList<SpecialItem>();
    foreach (var item in genericList)  // Uses IEnmerable<SpecialItem>, OK!
    {
        Console.WriteLine(item.Title);
    }

    var l = genericList.ToList();  // ERROR!

The ForEarch gets the correct Enumerator (SpecialItem), but the lambda does not know what to use (IEnumerable<BaseItem> or IEnumerable<SpecialItem>).

What to do? How can I set IEnumerable<SpecialItem> as 'default' interface? I dont want to explicetly code the type all the time like this:

var l = genericList.ToList<SpecialItem>();

Upvotes: 2

Views: 1379

Answers (2)

co-logic
co-logic

Reputation: 65

Thanks to Sriram Sakthivel, he guided me to a solution with a very small overhead. To make things clear I wanted to make sure that:

  1. Both lists, the generic and nongeneric one must be the same object. Therefore I have to derive, not packing in a wrapper.
  2. Both lists must support access via loops (ForEach) and lambdas / extension methods without the need to explicitly typing the class name.
  3. They have to implement IList<T>, so T out is not an option.

In short, the following code must compile without errors:

// Generic
var genericList = new GenericList<SpecialItem>();
foreach (var item in genericList)
{
    Console.WriteLine(item.Title);
}

var l = genericList.ToList();

// Nongeneric
var nongenericList = genericList as NongenericBaseList;
foreach (var item in nongenericList)
{
    Console.WriteLine(item.Name);
}

var nl = nongenericList.ToList();

I came to the conclusion, that this is not possible with the upper code (correct me if that is not true!). The loops are working fine, but either the generic or the nongeneric list does not work with .ToList() or other extension methods, because the compiler cannot inferre the type.

Now I used Sriram Sakthivel tipp, implementing only IEnumerable without <T>. But that allone would make it impossible to use extension methods at all even if you explicitely write the type.

I simply added a property, casting the collection:

public class NongenericBaseList : IEnumerable  // Without the T!
{
    protected List<BaseItem> _items;

    // The property
    public IEnumerable<BaseItem> L
    {
        get { return this as IEnumerable<BaseItem>; }
    }

    public BaseItem this[int index]
    {
        get { return _items[index]; }
    }

    public IEnumerator<BaseItem> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

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

Now I can type:

var nl = nongenericList.L.ToList();

Any better solution would be appreciated!

Upvotes: 0

Bas
Bas

Reputation: 27095

First of all: kudos for providing a self-contained example!

You cannot specify a 'default' interface for type inference. The argument type for ToList<T> cannot be resolved because it is ambiguous, the type implements both IEnumerable<BaseItem> and IEnumerable<SpecialItem>, and both versions are applicable.

Is there a possibility to remove the class NongenericBaseList completely, and use the GenericList<T>instead? That would solve your problem; you can use GenericList<BaseItem> instead of NongenericBaseList

Another option is to reverse the inheritance; make NongenericBaseList empty and deriving from GenericList<BaseItem>.

Upvotes: 1

Related Questions