为什么List要使用显式接口方法实现来实现非泛型接口方法?

Why does List use an explicit interface method implementation to implement non-generic interface methods?

// Some interface method signature

public interface IList : ICollection {
   ...
   bool Contains(Object value);
   ...
}

public interface IList<T> : ICollection<T> { ... }

public interface ICollection<T> : IEnumerable<T> {
   ...
   bool Contains(T item);
   ...
}

下面是List的源代码:https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs

public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T> {
   ...
   public bool Contains(T item) {
      ...
   }

   bool System.Collections.IList.Contains(Object item) {
      ...
   }
}

可以看到List使用显式接口方法实现(显式指定接口名称)来实现非泛型Contains。但是我对显式接口方法实现的了解是,只有当有两个具有相同签名的接口方法时才这样做。

但对于 public bool Contains(T item)public bool Contains(Object item),它们是不同的方法,因为它们具有不同的签名(通用参数和非通用参数),因此 List 将被实现为:

public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T> {
   public bool Contains(T item) { ... }

   public bool Contains(Object item) { ... }
}

那为什么List要使用显式接口方法实现来实现非泛型接口方法呢?我看不出在这种情况下使用显式接口方法实现有任何好处。我在这里遗漏了什么吗?

You can see that List uses explicit interface method implementation (explicitly specify the interface's name) to implement the non-generic Contains

确实如此。

但那是因为 IList 接口(而不是 IList<T>)已有数十年的历史 - 从 .NET 支持泛型(.NET 1.0 于 2001 年推出)之前的原始、黑暗时代开始- 直到 2005 年的 .NET Framework 2.0 才添加泛型)。那真是一个被上帝遗弃的时代。

List<T>(但不是 IList<T>)实现了 IList 接口,以便接受 [=15= 的旧代码可以使用新的通用 List<T> ](以一种允许保留对象身份并且不需要分配单独的IList实例的方式)。

假设现在是 2005 年,您正在编写一些奇妙的 C# 2.0 代码,这些代码使用了奇特的新具体化泛型和 List<T> - 但您需要与上次更新于 2004 年的库进行交互:

public void YourNewSexyGenericCode()
{
    List<String> listOfString = new List<String>() { "a", "b", "c" };
    
    OldAndBustedNET10CodeFrom2004( listOfString ); // <-- This works because List<String> implements IList.

    foreach( String s in listOfString ) Console.WriteLine( s );
}

public void OldAndBustedNET10CodeFrom2004( IList listOfString )
{
    listOfString.Add( "foo" );
    listOfString.Add( "bar" );
    listOfString.Add( "baz" );
    return;
}

如果 List<T> 没有实现 IList 那么你将不得不做这样可怕的事情:

    List<String> listOfString = new List<String>() { "a", "b", "c" };
    
    // Step 1: Create a new separate IList and copy everything from your generic list into it.
    IList classicList = new ArrayList();
    classicList.AddRange( listOfString );

    // Step 2: Pass the IList in:
    OldAndBustedNET10CodeFrom2004( classicList );

    // Step 3: Copy the elements back, in a type-safe manner:
    foreach( Object maybeString in classicList )
    {
        String asString = maybeString as String; // The `is String str` syntax wasn't available back in C# 2.0
        if( asString != null )
        {
            listOfString.Add( asString );
        }
    }

    // Step 4: Continue:
    foreach( String s in listOfString ) Console.WriteLine( s );

But what I know about explicit interface method implementation is, you only do it when there are two interface methods that have the same signature.

你错了。除了实现冲突的接口(例如隐藏 internal 接口的实现,实现类型理论 不合理的接口实现之外,还有 许多原因 选择显式接口实现 older/legacy 出于兼容性原因(如 IList)和美学原因(减少 API 混乱,尽管 EditorBrowsable 应该 用于此)。

But for public bool Contains(T item) and public bool Contains(Object item), they are different methods because they have different signatures (generic parameter and non-generic parameter), so List would have been implemented as...

在上面的段落中,我注意到 IList 是一个 type-theoretic unsound 接口,即:它允许你做毫无意义的事情and/or有害的东西,例如:

List<String> listOfString = new List<String>() { "a", "b", "c" };
IList asIList = listOfString;
asIList.Add( new Person() );

编译器会让这种情况发生,但它会在运行时崩溃,因为 List<String> 不能包含 Person。由于 .NET 对协变和逆变的支持,这可以通过 化泛型 解决(这就是为什么你可以 安全 隐式转换任何 List<String>IEnumerable<Object>,因为 String : Object,即使 List<T> 实际上没有实现 IEnumerable<Object>,但它确实实现了 IEnumerable<T>).

Then why does List use an explicit interface method implementation to implement non-generic interface methods?

因为 IList 是一个糟糕的界面,今天没有人应该使用它,但有些人 were/are 由于旧版兼容性而被迫使用它要求。每个人都希望看到这些遗留接口消失(尤其是我自己),但我们不能,因为它会破坏二进制应用程序的兼容性,这对于任何运行时或平台在 SWE 生态系统中生存都是必不可少的(这也是为什么 .NET 团队不幸的是拒绝许多 有意义 但会破坏现有编译程序的频繁请求(例如使 IList<T> 扩展 IReadOnlyList<T>)。

I cannot see any benefit to use explicit interface method implementation in this scenario. Am I missing something here?

遗漏了一些东西 - 我希望我的回答能启发您的想法。