Разрешение перегрузки C# с IList и IReadOnlyList
у меня есть метод, который я хотел бы взять все объекты, подобные списку, в моем решении. До .NET 4.5 это было просто:
public static T Method<T>(IList<T> list)
{
// elided
}
однако .NET 4.5 представил IReadOnlyList<T>
, к которому этот метод также должен применяться.
Я не могу просто изменить подпись, чтобы взять IReadOnlyList<T>
, поскольку есть места, где я применяю метод к чему-то конкретно набранному как IList<T>
.
алгоритм не может работать на IEnumerable<T>
, и он используется слишком часто (и слишком большие объекты) взять IEnumerable<T>
и создать новый List<T>
при каждом вызове.
Я попытался добавить перегрузку:
public static T Method<T>(IReadOnlyList<T> list)
{
// elided
}
... но это не будет компилироваться ни для чего, что реализует оба интерфейса (T[]
, List<T>
и многие другие типы), поскольку компилятор не может определить, какой метод использовать (особенно раздражает, поскольку они имеют одно и то же тело, поэтому это не имеет значения).
Я не хочу добавлять перегрузки Method
где взять T[]
, и List<T>
, и каждый другой тип, который реализует оба интерфейса.
как я должен это сделать?
4 ответов
Это может быть один из тех случаев, когда на самом деле проверка типа среды выполнения полезна:
public static T Method<T>(IEnumerable<T> source)
{
if (source is IList<T> list)
return Method(list);
if (source is IReadOnlyList<T> readOnly)
return Method(readOnly);
return Method(source.ToList() as IList<T>);
}
private static T Method<T>(IReadOnlyList<T> list) { ... }
private static T Method<T>(IList<T> list) { ... }
вам все равно придется дублировать код в том смысле, что вам нужны отдельные реализации для IList
и IReadOnlyList
потому что нет общего интерфейса, который вы можете использовать, но вы, по крайней мере, избегаете неоднозначной проблемы вызова.
ваш вероятный лучший выбор - сделать глобальный поиск и заменить IList
до IReadOnlyList
. Если нет ошибок компиляции, то вы должны быть в порядке.
вы должны получать только ошибки компилятора, если вы используете IList.Add
- что в любом случае безрассудно, так как массивы не поддерживают Add
.
можете ли вы изменить код вызова метода? Что делать, если вы создадите такой метод:
public static T1 Method<T1, T2>(T2 list) where T2 : IList<T1>, IReadOnlyList<T1>
{
return default(T1);
}
в этом случае вызовы выглядят следующим образом:
List<string> listA = new List<String>();
ReadOnlyCollection<string> listB = listA.AsReadOnly();
string outVar1 = Method<string, List<string>>(listA);
string outVar2 = Method<string, ReadOnlyCollection<string>>(listB);
другой способ создать два метода расширения для IList и IReadOnlyList таким образом:
public static T Test<T>(this IList<T> source)
{
return default(T);
}
public static T Test<T>(this IReadOnlyList<T> source)
{
return default(T);
}
и называйте их так:
string outVar1 = (listA as IReadOnlyList<string>).Test();
string outVar2 = (listB as IList<string>).Test();
возможно, ваше лучшее решение-посмотреть, почему ваш алгоритм не может работать на IEnumerable и изменить это. Вы используете IList<T>
или IReadOnlyList<T>
- конкретные члены, которые можно заменить членами, доступными в IEnumerable<T>
? Например:
// instead of
int c = list.Count;
// use
int c = list.Count();
EDIT: игнорируйте глупости ниже. Я оставляю это, чтобы комментарии продолжали иметь смысл.
вы не должны реализовать как IList<T>
и IReadOnlyList<T>
в любом классе. Единственные дополнительные члены в IList
спецификация предназначена для записи в список. Вы не должны делать это, если ваш список только для чтения. Я думаю, вам нужно изменить любые классы, которые реализуют оба, чтобы при их использовании можно было выбрать правильный метод.