当前位置: 首页 > news >正文

wordpress做过的大型网站吗服饰工厂网站建设

wordpress做过的大型网站吗,服饰工厂网站建设,网站设置一键登录怎么办,wordpress后台管理面板的主题文章目录协变协变接口的实现逆变里氏替换原则协变 协变概念令人费解,多半是取名或者翻译的锅,其实是很容易理解的。 比如大街上有一只狗,我说大家快看,这有一只动物!这个非常自然,虽然动物并不严格等于狗…

文章目录

    • 协变
    • 协变接口的实现
    • 逆变
    • 里氏替换原则

协变

协变概念令人费解,多半是取名或者翻译的锅,其实是很容易理解的。

比如大街上有一只狗,我说大家快看,这有一只动物!这个非常自然,虽然动物并不严格等于狗,但不会有人觉得我说的不对,把狗变成动物就是协变,C#也支持这个:

// C#6顶级语句
Dog dog= new Dog();
Animal animal= dog;interface Animal
{}class Dog : Animal
{}

那么接下来,大街上有一群狗,我说有一群动物,按理说也是对的,但看样子C#不这么认为

List<Dog> dogLst = new List<Dog>();
List<Animal> aniLst = dogLst;       //飙红飙红飙红了
interface Animal {}
class Dog : Animal {}

原因其实很容易理解,毕竟在上述的代码中,写了Dog:Animal,即声明了狗是动物的子类,但是并没有写List<Animal> : List<Dog>,换言之,从来没有声明过一群狗是一群动物的子类。

但是,如果不用List,而用其父类IEnumerable,写成下面这样,就又不报错了。

List<Dog> dogLst = new List<Dog>();
IEnumerable<Animal> aniLst = dogLst;

换言之,C#承认List<Dog>IEnumerable<Animal>的子类,个中差别,只需一览源码,就会知晓:

public interface IEnumerable<out T> : IEnumerable
public class List<T> : ..., IEnumerable<T>, ...

IEnumerable无非比List多了一个out参数,有了这个参数,就拥有了协变的功能,从而当UT的子类时,可以支持IEnumerable<U>IEnumerable<T>的转换。

在官方文档中,指明了具有out关键字的泛型接口包括IEnumerable<T>, IEnumerator<T>, IQueryable<T>IGrouping<TKey,TElement>

协变接口的实现

协变和逆变目前只能在泛型接口和委托中使用,下面新建一个泛型接口,并使用关键字out。由于使用.Net6.0的顶级语句,所以接口和类的声明放在后面。

IOut<string> outStr = new Out();
IOut<object> outObj = outStr;
Console.WriteLine(outObj.getName());interface IOut<out T>
{T getName();
}class Out : IOut<string>
{public string getName(){return GetType().Name;}
}

编译运行,最后输出Out,即outObj尽管在声明的时候用的是IOut<object>,但在IOutout修饰符的作用下,成功让IOut<object>变成了IOut<string>的父类,得以顺利调用Out中的方法。

那么接下来,如果想让getName更加完备一些,例如要求实现getName(T name)这样的功能,那么经out修饰的协变接口就无能为力了,像下面这样的写法果然被无情地飙红了

interface IOut<out T>
{void getName(T name);
}

逆变

VS作为宇宙顶级IDE,协变逆变十分拎得清,上述代码在飙红的同时,直接给出如下错误

变型无效: 类型参数“T”必须是在“IOut.getName(T)”上有效的 逆变式。“T”为 协变。

换言之,如果想让泛型接口可以输入泛型参数,那么需要用到逆变,具体写法如下,其中修饰符in表示逆变

IIn<object> inObj = new In();
IIn<string> inStr = inObj;
inStr.getName("in");interface IIn<in T>
{void getName(T name);
}class In : IIn<object>
{public void getName(object name){Console.WriteLine(name);}
}

逆变和协变最大的不同,并非inout这两个修饰符的字数,而是整个替换逻辑发生了变化,上述代码中,实际上是作为子类的string调用了通过父类object作为参数定义的函数。

里氏替换原则

在具体实现了协变与逆变之后,总觉得那里怪怪的,最怪的其实还是下面这行代码的错误

错错错错错错错错错错错错错错错错错错错错错错错错错错
interface IOut<out T>
{void getName(T name);
}

而且可以想象,与之相对应的下面的逆变代码也是不对的

错错错错错错错错错错错错错错错错错错错错错错错错错错
interface IOut<in T>
{T getName();
}

接下来复盘一下产生这种现象的原因,为了破除命名带来的困扰,接下来考虑泛型接口I<T>,其中有一个函数T test(T t)。现有两个特定的继承自泛型接口I<A>I<B>的类,假设I<A>要调用I<B>中的方法,那么其流程如下

  1. A I<A>.test(A t),即输入一个A类型的参数
  2. 将这个A类型的参数t,传入到B I<B>.test(B t)。由于I<B>要求输入B类型的参数,所以要求A可以转换为B类型。
  3. B I<B>.test(B t)计算完毕,返回一个B类型的参数
  4. 这个B类型的参数又被返回给最初的调用者A I<A>.test,而这时I<A>的函数最终将返回一个A类型的参数,换言之,在这个步骤,要求B可以转换为A

A能转为B,然后还得B能转为A,同时AB还不相等,这显然是不可能的。

所以逆变和协变分别实现了第2步和第4步。

如果I<A>想要调用I<B>test(B t)中的函数,那么A类型必须可以转成B类型。正如string可以转为object一样,此即逆变,用in修饰,其作用场合为子类调用父类中的方法。

如果I<A>想要调用B I<B>test(),那么作为返回值的B类型必须可以转化为A类型,此即协变,用out修饰,正是父类调用子类的方法。

协变和逆变的统一之处在于,二者都严格遵循这子类可以转变为父类的规则,此即里氏替换。这是1987年,芭芭拉·利斯科夫提出的,她也是2008年图灵奖得主。

在协变逆变的过程中,对里氏替换的遵循主要表现在当子类方法重载父类方法时

  • 方法的输入参数要更加宽松,此即逆变(IOut<object>调用IOut<string>objectstring更宽松)
  • 方法的返回值要更加严格,此即协变(IOut<string>调用IOut<object>stringobject更严格)
http://www.yayakq.cn/news/511870/

相关文章:

  • 网站建设在线推广wordpress 随机图片
  • 企业手机网站设计wordpress经典
  • 赣州市住房和城乡建设局网站wordpress dux主题首页更新
  • 杭州公司网站网站优化方案设计
  • 网站制作新报价北京建筑职业培训网
  • 分享经济网站怎么建设怎样用ps做网站
  • 新安县住房和城乡建设局网站做环球资源网站有没有效果
  • 文登区建设局网站wordpress侧边栏标题字数
  • 亿唐网不做网站做品牌案例分析品牌策划公司介绍
  • 网站如何添加图标免费推广网站入口2023燕
  • 潍坊网站建设解决方案wordpress 分块首页
  • 珠海网站推广微网站管理
  • 兼职做效果图的网站htp免费域名注册网站
  • 同城网站开发站内优化seo
  • 网站设计模板简约哪个汽车网站好
  • 贵阳公司做网站wordpress 4.0 伪静态
  • 免费个人主页网站如何做网站外部链接
  • 建立个人网站有什么好处2021企业公司大黄页
  • 推广网站的方法有搜索引擎营销wordpress去掉仪表盘
  • 做课件网站深圳网站建房
  • 做网站小程序多少钱电子商务是干什么的工作
  • 盘锦做网站公司怎么找专业的营销团队
  • 上海建设网站平台做网站代码保密协议
  • 网站建设记录过程做网站难吗?
  • 旅行网站建设的规划书广西智能网站建设平台
  • 重庆网站seo搜索引擎优化unix系统安装wordpress
  • 功能类似淘宝的网站建设张家界工程建设信息网站
  • 大型网站建设动力无限三合一网站开发有什么区别
  • 推进门户网站建设seo岗位是什么意思
  • 做seo网站要多少钱行业网站 cms