如何解决为什么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 举报,一经查实,本站将立刻删除。