强制列表<base /> = List <derived>&amp; Act Like List <derived>

时间:2017-03-23 14:55:30

标签: c# list class generics covariance

public class Building
{
    public string Name = "Not To Be Seen";
    ...
}

public class School: Building
{
    public int NoOfRooms = 200;
    public string Address = "123 Main St.";
    ...
}

目标(n其他一些类/用例)

// This is a simple example, in reality this code is far more complex
// the class "School" is private from the program
List<Building> city = new List<School>();
// city will only have properties of the class School (or at least those are the only properties seen)

Console.WriteLine(city[0].NoOfRooms.ToString()) // Outputs 200
Console.WriteLine(city[0].Name) // Should not output anything

根据正确转换列表,这似乎应该是非常可能的。但是,我似乎无法弄清楚如何让它工作。它似乎涉及协方差,但我想要一个不可变的列表或类型。 C#不容易提供这种转换(即基类可以完全模仿派生类)?

由于

2 个答案:

答案 0 :(得分:0)

更新 - 由于您完全更改了示例,我将更新我的回答以反映新示例。我这样做只有一次;所以对于任何阅读的人来说,如果我的回答似乎与问题不一致,那是因为OP在编辑期间进行了不必要的更改。

这里有一些问题。 List<Base>变量无法引用List<Derived>实例。这是因为,例如,List<Base>必须实施将Base实例添加到List的操作,而List<Derived>不能支持此类操作。

更一般地说,引用类型(声明变量的类型)定义了调用代码可以使用该值的接口。因此,如果您需要在示例中说出city[0].NoOfRooms,则必须将city声明为List<School>School仍然可以存储在List<Building>中,但是当您从此类列表中获取项目时,您必须先尝试转换为School才能使用NoOfRooms属性。 (并且应该注意的是,如果您发现自己这样做,则表明存在设计错误。)

问题的另一部分与隐藏Name属性有关。

在某些语言中,你可以通过&#34;私人继承获得这样的效果&#34; - 但这不是C#中的事情。您可以做的最接近的事情是从School派生Building 而不是,但可能包含(私有)Building类型的字段或属性。然后,当然,根本不可能在School中存储List<Building>

另一种方法是让School使用一个不返回任何内容的getter覆盖Name属性,这实际上更接近代码注释所暗示的内容。

答案 1 :(得分:0)

根据您的问题的评论,List<Building>中的对象不能被任意强制转换为School,除非它最初被构造为School对象(或派生类)来自School)。考虑如果你有另一个名为Hospital的类派生自Building并且有自己的属性说NumberOfPatients并且这是该列表中的一个对象会发生什么。您无法将Hospital个实例投射到School并仅在School上调用属性 - 这样做是没有意义的。

也就是说,您仍然可以List<Building>存储任何类型的Building个对象,包括派生的对象(这样您就会有一个包含Building的混合列表,SchoolHospital个对象)。然后,您可以通过Linq OfType获取特定派生类型的对象的枚举(添加到文件的顶部using System.Linq;)。因此,类似buildingList.OfType<School>()的内容只会为您提供School个对象的枚举,您现在可以迭代这些对象并调用仅在School上可用的方法/属性。