John
John

Reputation: 1522

Is is possible to apply a generic method to a list of items?

Lets say I've written my own method to reverse a list in place.

public static void MyReverse<T>(List<T> source)
{
    var length = source.Count;
    var hLength = length / 2;
    for (var i = 0; i < hLength; i++)
    {
        T temp = source[i];
        source[i] = source[length - 1 - i];
        source[length - 1 - i] = temp;
    }
}

I call it like so, and it works.

var fooList = new List<Foo>();
MyReverse(fooList);

If I want to reverse multiple lists, I call it like so.

var fooList = new List<Foo>();
var barList = new List<Bar>();
var bazList = new List<Baz>();
MyReverse(fooList);
MyReverse(barList);
MyReverse(bazList);

If I want to reverse an arbitrary number of lists, I'd try:

public static void Main(string[] args)
{
    var lists = new List<object>
    {
        new List<Foo>(),
        new List<Bar>(),
        new List<Bar>()
    };

    ReverseLists(lists);
}

public static void ReverseLists(List<object> sourceLists)
{
    foreach (var sourceList in sourceLists)
    {
        MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage
    }
}

But this throws a compile time error. Is what I'm trying to do possible - could the ReverseLists method be implemented?

Upvotes: 15

Views: 620

Answers (5)

arbitrary
arbitrary

Reputation: 103

change this line in the original post:

MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage

to this:

MyReverse(((System.Collections.IList)sourceList).Cast<object>().ToList());

Upvotes: 0

Nathan
Nathan

Reputation: 6216

If you change your ReverseLists method signature to

public static void ReverseLists<T>(IEnumerable<object> sourceLists)
{
    foreach (var sourceList in sourceLists.OfType<List<T>>())
    {
        MyReverse(sourceList);
    }
}

Then you can call this for each list type:

ReverseLists<Foo>(lists);
ReverseLists<Bar>(lists);

Maybe not ideal having to call it once per list type, but a comparatively small change to your existing code.

Upvotes: 2

Wolfwyrd
Wolfwyrd

Reputation: 15916

To compliment the answer from Sriram Sakthivel - the key to your problem is that the Type cannot be inferred from what you are passing in. It's worth noting that List<T> implements IList so your issue above can be reframed using a parameter array of List like:

void Main()
{
    var fooList = new List<string>();
    var barList = new List<string>();
    var bazList = new List<string>();
    ReverseLists(fooList, barList, bazList);
}

public static void ReverseLists(params IList [] sourceLists)
{
    foreach (var sourceList in sourceLists)
    {
        MyReverse(sourceList); 
    }
}

public static void MyReverse(IList source)
{
    var length = source.Count;
    var hLength = length / 2;
    for (var i = 0; i < hLength; i++)
    {
        var temp = source[i];
        source[i] = source[length - 1 - i];
        source[length - 1 - i] = temp;
    }
}

Upvotes: 2

James S
James S

Reputation: 3588

As per my comment above...

The compiler cannot covert infer the type of T when passed object (which is effectively what's happening)

However there is a much simpler option - which is to just abandon using generics, and change the signature of your MyReverse method to public static void MyReverse(IList source) (and elsewhere replace List<object> with IList)

ie:

public static void Main(string args[])
{
    var lists = new List<IList>
    {
        new List<Foo>(),
        new List<Bar>(),
        new List<Bar>()
    };

    ReverseLists(lists);
}

public static void MyReverse(IList source)
{
    var length = source.Count;
    var hLength = length / 2;
    for (var i = 0; i < hLength; i++)
    {
        var temp = source[i];
        source[i] = source[length - 1 - i];
        source[length - 1 - i] = temp;
    }
}

public static void ReverseLists(List<IList> sourceLists)
{
    foreach (var sourceList in sourceLists)
    {
        MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage
    }
}
public class Foo
{
}
public class Bar
{
}

Upvotes: 7

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73442

Assuming you have a static method like this

public static class ReverseHelper
{
    public static void MyReverse<T>(IList<T> source)
    {
        var length = source.Count;
        var hLength = length / 2;
        for (var i = 0; i < hLength; i++)
        {
            T temp = source[i];
            source[i] = source[length - 1 - i];
            source[length - 1 - i] = temp;
        }
    }
}

With the help of non generic interface and a generic class you can do it.

public interface IReverser
{
    void Reverse();
}
public class ListReverser<T> : IReverser
{
    private readonly IList<T> source;
    public ListReverser(IList<T> source)
    {
        this.source = source;
    }
    public void Reverse()
    {
        ReverseHelper.MyReverse<T>(source);
    }
}
static void Main(string[] args)
{
    var lists = new List<IReverser>
    {
        new ListReverser<Foo>(new List<Foo>()),
        new ListReverser<Bar>(new List<Bar>()),
        new ListReverser<Bar>(new List<Bar>())
    };

    foreach (var reverser in lists)
    {
        reverser.Reverse();
    }
}

I've used IList<T> as opposed to List<T> to support broader number of types; If you want List<T> you can put it back.

Upvotes: 10

Related Questions