Можно ли применить общий метод к списку элементов?

Предположим, что я написал свой собственный метод для изменения списка на месте.

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;
    }
}

Я называю это так, и он работает.

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

Если я хочу отменить несколько списков, я называю это так.

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

Если я хочу изменить произвольное количество списков, попробую:

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
    }
}

Но это порождает ошибку времени компиляции. Это то, что я пытаюсь сделать возможным - может ли быть реализован метод ReverseLists?

Ответы

Ответ 1

Предполагая, что у вас есть статический метод, подобный этому

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;
        }
    }
}

С помощью не общего интерфейса и общего класса вы можете это сделать.

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();
    }
}

Я использовал IList<T> вместо List<T> для поддержки более широкого числа типов; Если вы хотите List<T>, вы можете вернуть его.

Ответ 2

Согласно моему комментарию выше...

Компилятор не может скрывать вывод типа T при передаче object (что фактически происходит)

Однако существует гораздо более простой вариант - просто отказаться от использования дженериков и изменить подпись вашего метода MyReverse на public static void MyReverse(IList source) (и в другом месте заменить List<object> на IList)

т

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
{
}

Ответ 3

Если вы измените свою сигнатуру метода ReverseLists на

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

Затем вы можете вызвать это для каждого типа списка:

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

Может быть, не обязательно иметь его один раз для типа списка, но сравнительно небольшое изменение в вашем существующем коде.

Ответ 4

Чтобы комплимент ответа от Шрирама Сактивеля - ключ к вашей проблеме состоит в том, что Тип не может быть выведен из того, что вы проходите. Стоит отметить, что List<T> реализует IList, поэтому ваш вопрос выше можно перерисовать с помощью массив параметров списка как:

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;
    }
}

Ответ 5

измените эту строку в исходном сообщении:

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

:

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