协变(List泛型作为方法参数时的父类子类问题)

DotNet NB

共 5363字,需浏览 11分钟

 ·

2024-05-30 08:00

有段时间没搞.net的项目了(没办法,谁让国内JAVA流行是事实)。最近又回归.net(哪里需要哪里搬~)。

接收到需求后,一顿输出,结果...咦?编译失败???

错误信息:

 CS1503:参数1:无法从"System.Collections.Generic.List<Student>" 转换为"Person"

 额...仔细回想下(好久不搞.net,.net的理论知识都已经落灰了)。

下面是完整的测试代码:

public class Person{    public string Name { getset; }    public int Age { get; set; }}public class Student :Person{    public string School { getset; }    public decimal Score { get; set; }}public abstract class Point{     public Person person { getset; }    public List<Person> persons { get; set; }    /// <summary>    /// 单个    /// </summary>    public abstract void Info();
/// <summary> /// 批量 /// </summary> public abstract void List();}
public class PointStudent : Point{ public PointStudent(Person data) { this.person = data;    } public PointStudent(List<Person> data) { this.persons = data;    } public override void Info() { Console.WriteLine($"{((Student)person).Name}就读于{((Student)person).School}");    } public override void List() { this.persons.ForEach(p => { this.person = p; Info(); }); }}
static void Main(string[] args){ Student student = new Student() { Name="张三",Age = 18,School ="东师"}; Point point = new PointStudent(student); point.Info();
List<Student> students = new List<Student>() { student }; Point pointList = new PointStudent(students); pointList.List();}

那么问题来了..

为什么使用子类的对象作为参数传入时没有错误?

答:

  1. 期绑定(Polymorphism):C# 支持后期绑定,这意味着在运行时确定方法的实际执行版本,而不是在编译时确定。

  2.  继承和多态性:子类继承了基类的所有特性,同时可以拥有自己的特性和行为。当你将子类对象传递给期望基类类型的方法时,方法将根据子类对象的实际类型执行相应的操作。


为什么使用List时报错?

答:

对于List<Person> List<Student> ,这是List 类型,可以理解为2个不相干的类,不存在继承关系,所以不能直接作为参数传入。

怎么解决?

答:

这里就用到了协变,  协变允许将一个派生类对象赋值给一个基类对象。

协变示例:

// Covariance.IEnumerable<string> strings = new List<string>();  // An object that is instantiated with a more derived type argument// is assigned to an object instantiated with a less derived type argument.// Assignment compatibility is preserved.IEnumerable<object> objects = strings;

从C#4.0开始,IEnumerable 的声明中引入了 out 关键字,它表示这个接口支持协变性。

[TypeDependency("System.SZArrayHelper")][__DynamicallyInvokable]public interface IEnumerable<out T> : IEnumerable{    [__DynamicallyInvokable]    new IEnumerator<T> GetEnumerator();}

那么上面这段代码该怎么修改?

public class Person{        public string Name { getset; }    public int Age { get; set; }}
public class Student :Person{        public string School { getset; } public decimal Score { get; set; }}
public abstract class Point{ public Person person { get; set; }
public IEnumerable<Person> persons { get; set; } /// <summary> /// 单个 /// </summary>        public abstract void Info(); /// <summary> /// 批量 /// </summary> public abstract void List();}
public class PointStudent : Point{ public PointStudent(Person data) { this.person = data;        } public PointStudent(IEnumerable<Person> data) { this.persons = data;        } public override void Info() { Console.WriteLine($"{((Student)person).Name}就读于{((Student)person).School}");        } public override void List() { this.persons.ForEach(p => { this.person = p; Info(); }); }}
static void Main(string[] args){ Student student = new Student() { Name="张三",Age = 18,School ="东师"}; Point point = new PointStudent(student); point.Info();
List<Student> students = new List<Student>() { student }; Point pointList = new PointStudent(students); pointList.List();}

其实代码改动比较简单,大多数的程序员都知道该怎么去调整,这里我就多啰嗦了,需要注意的是:在岗位上拧螺丝的时候,不要忘了去回顾下自己的知识栈,保持技术的鲜活~




浏览 14
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报