使用 IList<T> 和 IReadOnlyList<T> 的 C# 重载解析

C# overload resolution with IList<T> and IReadOnlyList<T>

我有一个方法,我想在我的解决方案中使用所有 list-like objects。在 .NET 4.5 之前,这很简单:

public static T Method<T>(IList<T> list)
{
    // elided
}

然而,.NET 4.5 引入了IReadOnlyList<T>,这种方法应该也适用。

我不能只更改签名以采用 IReadOnlyList<T>,因为在某些地方我将该方法应用于专门键入为 IList<T> 的内容。

该算法无法在 IEnumerable<T> 上 运行,而且它的使用过于频繁(并且 objects 太大)无法获取 IEnumerable<T> 并创建一个新的List<T> 每次调用。

我试过添加重载:

public static T Method<T>(IReadOnlyList<T> list)
{
    // elided
}

... 但是这不会编译任何实现两个接口(T[]List<T> 和许多其他类型)的东西,因为编译器无法确定使用哪种方法(特别烦人,因为他们有相同的body,所以没关系)。

我不想添加需要 T[]List<T>Method 的重载,以及实现这两个接口的所有其他类型。

我该如何完成?

您可能最好的选择是进行全局搜索并将 IList 替换为 IReadOnlyList。如果没有编译错误那么你应该没问题。

只有在使用 IList.Add 时才会收到编译器错误 - 无论如何这是有勇无谋的,因为数组不支持 Add.

也许您最好的解决方案是研究为什么您的算法不能在 IEnumerable 上 运行 并更改它。您是否正在使用 IList<T>IReadOnlyList<T> 特定成员,您可以用 IEnumerable<T> 中可用的成员替换这些成员?例如:

// instead of
int c = list.Count;

// use
int c = list.Count();

编辑:忽略下面的废话。我离开它是为了让评论继续有意义。

您不应在任何 class 中同时实施 IList<T>IReadOnlyList<T>IList 规范中唯一的附加成员用于写入列表。如果您的列表是只读的,则不需要这样做。我认为您需要更改实现两者的任何 classes,以便在使用它们时可以选择正确的方法。

但是,由于 IReadOnlyList<T> 的所有成员都包含在 IList<T> 中(以及从 IReadOnlyCollection<T> 派生的成员),我想知道 .Net 中的 IList<T> 是否真的应该进行更改,以便它继承 IReadOnlyList<T> 接口而不是复制成员。不是那个现在对你有帮助。

能改一下方法调用的代码吗? 如果您创建这样的方法会怎样:

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

这可能是实际检查运行时类型有用的情况之一:

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

你仍然需要复制代码,因为你需要为 IListIReadOnlyList 单独实现,因为没有你可以利用的通用接口,但你至少避免了模棱两可的调用问题.