c# - 为什么List使用显式接口(interface)方法实现来实现非泛型接口(interface)方法?

标签 c# .net explicit-interface

// 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使用显式接口(interface)方法实现(显式指定接口(interface)名称)来实现非泛型Contains。但我对显式接口(interface)方法实现的了解是,只有当两个接口(interface)方法具有相同签名时才需要这样做。

但是对于 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为什么要使用显式接口(interface)方法实现来实现非泛型接口(interface)方法呢?我看不出在这种情况下使用显式接口(interface)方法实现有任何好处。我在这里遗漏了什么吗?

最佳答案

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

确实。

但那是因为 IList 接口(interface)(不是 IList<T> )已经有几十年的历史了 - 从原始的、黑暗的时代开始,在 .NET 支持泛型之前(.NET 1.0 于 2001 年问世 - 直到 .NET 才添加泛型) 2005 年框架 2.0)。那真是一个被上帝遗弃的时代。

List<T> (但不是 IList<T> )实现 IList 接口(interface),以便新的通用 List<T> 可以被接受 IList 的旧代码使用(以允许保留对象身份且无需分配单独的 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.

你错了。除了实现冲突的接口(interface)之外,选择显式接口(interface)实现的原因还有很多(例如隐藏 internal 接口(interface)的实现、实现类型理论不健全的旧/遗留接口(interface))兼容性原因(如 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 是一个类型理论不健全接口(interface),即:它允许你做无意义和/或有害的事情,示例:

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 是一个糟糕的接口(interface),今天没有人应该使用它,但由于遗留兼容性要求,有些人被迫使用它。每个人都希望看到这些遗留接口(interface)消失(尤其是我自己),但我们不能,因为这会破坏二进制应用程序兼容性,而这对于任何运行时或平台在 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?

遗漏了一些东西 - 我希望我的回答能够照亮您的想法。

关于c# - 为什么List使用显式接口(interface)方法实现来实现非泛型接口(interface)方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66286379/

相关文章:

c# - 为什么在显式 getter-only 接口(interface)实现上使用私有(private) setter 是非法的?

.net - NuGet Packager Task 在 VSTS 中使用错误的 MSBuild?

pointers - 通过类型/种类/等级以外的方式区分 Fortran 中的泛型

c# - C#多线程服务器-通用客户端集合可能是一个问题

c# - LINQ 左外部联接 - 对象引用未设置为对象的实例

c# - 将结构作为参数传递给从 F# 调用的 C 函数

c# - Try/Finally(没有 Catch)会冒泡异常吗?

c# - 如何使用反射来获取显式实现接口(interface)的属性?

c# - 为什么在 C# 中不允许这样做?

c# - 投影仪控制/显示 C#