微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为什么List使用显式接口方法实现来实现非泛型接口方法?

// 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>,IReadOnlyList<T> {
   public bool Contains(T item) { ... }

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

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

解决方法

可以看到List使用显式接口方法实现(显式指定接口名称)来实现非泛型的Contains

确实如此。

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

List<T>(但不是 IList<T>)实现了 IList 接口,以便新的泛型 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","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 );

但我对显式接口方法实现的了解是,只有当有两个具有相同签名的接口方法时,你才这样做。

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

但是对于 public bool Contains(T item)public bool Contains(Object item),它们是不同的方法,因为它们具有不同的签名(泛型参数和非泛型参数),所以 List 会被实现为...

在上面的段落中,我注意到 IList 是一个类型理论不健全的接口,即:它允许您做无意义和/或有害的事情 em>,例如:

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

编译器会允许这种情况发生,但它会在运行时崩溃,因为 List<String> 不能包含 Person。由于 .NET 支持协变和逆变(这就是为什么您可以安全地将任何 List<String> 隐式转换为 IEnumerable<Object> ,因为 String : Object,尽管 List<T> 实际上没有实现 IEnumerable<Object>,但它确实实现了 IEnumerable<T>)。

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

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

我看不出在这种情况下使用显式接口方法实现有什么好处。我在这里遗漏了什么吗?

遗漏了一些东西 - 我希望我的回答能照亮你的心。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。