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

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

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

错误信息:

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

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

下面是完整的测试代码:

csharp 复制代码
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
public class Student :Person
{
    public string School { get; set; }
    public decimal Score { get; set; }
}
public abstract class Point
{ 
    public Person person { get; set; }
    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();
}

那么问题来了...

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

答:

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

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

为什么使用List时报错?

答:

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

怎么解决?

答:

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

协变示例:

csharp 复制代码
// 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 关键字,它表示这个接口支持协变性。

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

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

csharp 复制代码
public class Person
{
        public string Name { get; set; }
    public int Age { get; set; }
}

public class Student :Person
{
        public string School { get; set; }
    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();
}

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


相关推荐
喵叔哟1 小时前
24.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--认证微服务
微服务·架构·.net
Java Fans3 小时前
在WPF项目中集成Python:Python.NET深度实战指南
python·.net·wpf
IGP93 小时前
20250606-C#知识:委托和事件
开发语言·c#
书中自有妍如玉4 小时前
.net 使用MQTT订阅消息
java·前端·.net
Kookoos4 小时前
ABP VNext 与 Neo4j:构建基于图数据库的高效关系查询
数据库·c#·.net·neo4j·abp vnext
我是唐青枫5 小时前
.NET AOT 详解
java·服务器·.net
张鱼小丸子_微辣5 小时前
.Net Framework 4/C# LINQ*
c#
..活宝..7 小时前
【Emgu CV教程】11.2、Scharr边缘检测
图像处理·计算机视觉·c#·emgu cv·图像分析
yngsqq7 小时前
事件监听 ——CAD C#二次开发
c#
The Kite7 小时前
MPLAB X IDE 软件安装与卸载
ide·c#·嵌入式