为什么通用 IList<>不继承非泛型 IList

本文介绍了为什么通用 IList<>不继承非泛型 IList的处理方法,对大家解决问题具有一定的参考价值

问题描述

IList 不继承 IList 其中 IEnumerable 继承 IEnumerable.>

如果out修饰符是唯一的原因,那么为什么大多数IList的实现(例如CollectionList) 实现了 IList 接口.

所以任何人都可以说好的,如果该语句对于 IList 的所有实现都是正确的,那么在必要时直接将其转换为 IList.但问题是虽然 IList 不继承 IList 所以不能保证每个 IList 对象都是 IList.

此外,使用 IList 显然不是解决方案,因为没有 out 修饰符泛型不能分配给较少继承的类;并且在这里创建 List 的新实例不是解决方案,因为有人可能希望将 IList 实际引用作为 IList 指针;并使用 List 代替 IList 实际上是一种糟糕的编程习惯,并不能达到所有目的.

如果 .NET 想要给 IList 的每个实现提供灵活性,不应该有非通用实现的契约(即 IList),那么为什么他们没有不保留另一个实现泛型和非泛型版本的接口,也没有建议所有想要为泛型和非遗传项签约的具体类都应该通过该接口签约.

ICollection 转换为 ICollection 并将 IDictionary 转换为 IDictionary 也会出现同样的问题>.

解决方案

如您所见,IList 中的 T 不是 协变.根据经验:任何可以修改其状态的类都不能是协变的.原因是这些类通常具有将 T 作为其参数之一的类型的方法,例如void Add(T element).输入位置不允许使用协变类型参数.

添加泛型,除其他原因外,是为了提供类型安全.例如,您不能将 Elephant 添加到 Apple 的列表中.如果 ICollection 要扩展 ICollection,那么您可以调用 ((ICollection)myApples).Add(someElephant) 而无需编译-时间错误,因为ICollection 有一个方法void Add(object obj),它似乎允许您将任何 对象添加到列表中,而在实践中你只能添加T的对象.因此,ICollection 不会扩展 ICollection 并且 IList 不会扩展 IList.>

Anders Hejlsberg,C# 的创造者之一,这样解释:

<块引用>

理想情况下,所有泛型集合接口(例如 ICollectionIList)都将从它们的非泛型对应物继承,以便泛型接口实例可以可用于泛型和非泛型代码.

事实证明,唯一可能的通用接口是 IEnumerable,因为只有 IEnumerable 是逆变的[sic1]:在IEnumerable中,类型参数T仅用于输出"位置(返回值)而不是输入"位置(参数).ICollectionIList 在输入和输出位置都使用 T,因此这些接口是不变的.

1) IEnumerableco-variant

<小时>

从 .Net 4.5 开始,有 IReadOnlyCollectionIReadOnlyList 协变接口.但是 IListICollection 和许多列表和集合类没有实现或扩展它们.坦率地说,我发现它们不是很有用,因为它们只定义了 Countthis[int index].

<小时>

如果我可以从头开始重新设计 .Net 4.5,我会将列表接口拆分为只读协变接口 IList,其中包括 ContainsIndexOf,以及一个可变的不变接口 IMutableList.然后你可以将 IList 转换为 IList.我在这里实现了这个:

<块引用>

M42 集合 - 协变集合、列表和数组.

IList<T> does not inherit IList where IEnumerable<out T> inherits IEnumerable.

If out modifier are the only reason then why most of the implementation of IList<T> (e.g. Collection<T>, List<T>) implements IList interface.

So any one can say OK, if that statements is true for all implementation of IList<T> then directly cast it to IList when necessary. But problem is that though IList<T> does not inherit IList so it is not guaranteed that every IList<T> object are IList.

Moreover using IList<object> is obviously not the solution because without out modifier generics can not be assigned to a less inherit class; and creating new instance of List is not a solution here because someone may want actual reference of the IList<T> as an IList pointer; and use List<T> insteed of IList<T> is actually a bad programming practice and doesn't serve all purpose.

If .NET wants to give flexibility that every implementation of IList<T> should not have a contract of non-generic implementation (i.e. IList) then why they didn't keep another interface which implement both generic and non-generic version and didn't suggest that all concrete class which want to contract for generic and non-genetic item should contract via that interface.

Same problem occurs for casting ICollection<T> to ICollection and IDictionary<TKey, TValue> to IDictionary.

解决方案

As you note, T in IList<T> is not covariant. As a rule of thumb: any class that can modify its state cannot be covariant. The reason is that such classes often have methods that have T as the type of one of their parameters, e.g. void Add(T element). And covariant type parameters are not allowed in input positions.

Generics were added, among other reasons, to provide type safety. For example, you can't add an Elephant to a list of Apple. If ICollection<T> were to extend ICollection, then you could call ((ICollection)myApples).Add(someElephant) without a compile-time error, as ICollection has a method void Add(object obj), which seemingly allows you to add any object to the list, while in practice you can only add objects of T. Therefore, ICollection<T> does not extend ICollection and IList<T> does not extend IList.

Anders Hejlsberg, one of the creators of C#, explains it like this:

Ideally all of the generic collection interfaces (e.g. ICollection<T>, IList<T>) would inherit from their non-generic counterparts such that generic interface instances could be used both with generic and non-generic code.

As it turns out, the only generic interface for which this is possible is IEnumerable<T>, because only IEnumerable<T> is contra-variant [sic1]: In IEnumerable<T>, the type parameter T is used only in "output" positions (return values) and not in "input" positions (parameters). ICollection<T> and IList<T> use T in both input and output positions, and those interfaces are therefore invariant.

1) IEnumerable<T> is co-variant


Since .Net 4.5 there are the IReadOnlyCollection<out T> and IReadOnlyList<out T> covariant interfaces. But IList<T>, ICollection<T> and many of the list and collection classes don't implement or extend them. Frankly, I find them not very useful, as they only define Count and this[int index].


If I could redesign .Net 4.5 from the ground up, I would have split the list interface into a read-only covariant interface IList<out T> that includes Contains and IndexOf, and a mutable invariant interface IMutableList<T>. Then you could cast IList<Apple> to IList<object>. I implemented this here:

M42 Collections - Covariant collections, lists and arrays.

这篇关于为什么通用 IList&lt;&gt;不继承非泛型 IList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2

查看全文
(adsbygoogle = window.adsbygoogle || []).push({});
相关文章
(adsbygoogle = window.adsbygoogle || []).push({});
var eskeys = '为什么,通用,ilist&lt,gt,不,继承,非,泛,型,ilist'; var cat = 'c';
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆
var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?0c3a090f7b3c4ad458ac1296cb5cc779"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); (function () { var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();

admin_action_{$_REQUEST[‘action’]}

do_action( "admin_action_{$_REQUEST[‘action’]}" )动作钩子::在发送“Action”请求变量时激发。Action Hook: Fires when an ‘action’ request variable is sent.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$_REQUEST['action']引用从GET或POST请求派生的操作。源码(Source)更新版本源码位置使用被使用2.6.0 wp-admin/admin.php:...

日期:2020-09-02 17:44:16 浏览:1173

admin_footer-{$GLOBALS[‘hook_suffix’]}

do_action( "admin_footer-{$GLOBALS[‘hook_suffix’]}", string $hook_suffix )操作挂钩:在默认页脚脚本之后打印脚本或数据。Action Hook: Print scripts or data after the default footer scripts.目录锚点:#说明#参数#源码说明(Description)钩子名的动态部分,$GLOBALS['hook_suffix']引用当前页的全局钩子后缀。参数(Parameters)参数类...

日期:2020-09-02 17:44:20 浏览:1071

customize_save_{$this->id_data[‘base’]}

do_action( "customize_save_{$this-&gt;id_data[‘base’]}", WP_Customize_Setting $this )动作钩子::在调用WP_Customize_Setting::save()方法时激发。Action Hook: Fires when the WP_Customize_Setting::save() method is called.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_data...

日期:2020-08-15 15:47:24 浏览:809

customize_value_{$this->id_data[‘base’]}

apply_filters( "customize_value_{$this-&gt;id_data[‘base’]}", mixed $default )过滤器::过滤未作为主题模式或选项处理的自定义设置值。Filter Hook: Filter a Customize setting value not handled as a theme_mod or option.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_date['base'],指的是设置...

日期:2020-08-15 15:47:24 浏览:901

get_comment_author_url

过滤钩子:过滤评论作者的URL。Filter Hook: Filters the comment author’s URL.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/comment-template.php:32610...

日期:2020-08-10 23:06:14 浏览:932

network_admin_edit_{$_GET[‘action’]}

do_action( "network_admin_edit_{$_GET[‘action’]}" )操作挂钩:启动请求的处理程序操作。Action Hook: Fires the requested handler action.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$u GET['action']引用请求的操作的名称。源码(Source)更新版本源码位置使用被使用3.1.0 wp-admin/network/edit.php:3600...

日期:2020-08-02 09:56:09 浏览:879

network_sites_updated_message_{$_GET[‘updated’]}

apply_filters( "network_sites_updated_message_{$_GET[‘updated’]}", string $msg )筛选器挂钩:在网络管理中筛选特定的非默认站点更新消息。Filter Hook: Filters a specific, non-default site-updated message in the Network admin.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分$_GET['updated']引用了非默认的...

日期:2020-08-02 09:56:03 浏览:865

pre_wp_is_site_initialized

过滤器::过滤在访问数据库之前是否初始化站点的检查。Filter Hook: Filters the check for whether a site is initialized before the database is accessed.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/ms-site.php:93910...

日期:2020-07-29 10:15:38 浏览:834

WordPress 的SEO 教学:如何在网站中加入关键字(Meta Keywords)与Meta 描述(Meta Description)?

你想在WordPress 中添加关键字和meta 描述吗?关键字和meta 描述使你能够提高网站的SEO。在本文中,我们将向你展示如何在WordPress 中正确添加关键字和meta 描述。为什么要在WordPress 中添加关键字和Meta 描述?关键字和说明让搜寻引擎更了解您的帖子和页面的内容。关键词是人们寻找您发布的内容时,可能会搜索的重要词语或片语。而Meta Description则是对你的页面和文章的简要描述。如果你想要了解更多关于中继标签的资讯,可以参考Google的说明。Meta 关键字和描...

日期:2020-10-03 21:18:25 浏览:1736

谷歌的SEO是什么

SEO (Search Engine Optimization)中文是搜寻引擎最佳化,意思近于「关键字自然排序」、「网站排名优化」。简言之,SEO是以搜索引擎(如Google、Bing)为曝光媒体的行销手法。例如搜寻「wordpress教学」,会看到本站的「WordPress教学:12个课程…」排行Google第一:关键字:wordpress教学、wordpress课程…若搜寻「网站架设」,则会看到另一个网页排名第1:关键字:网站架设、架站…以上两个网页,每月从搜寻引擎导入自然流量,达2万4千:每月「有机搜...

日期:2020-10-30 17:23:57 浏览:1309