【问题标题】:Force List<Base> = List<Derived> & Act Like List<Derived> [duplicate]强制列表<Base> = List<Derived> & 像 List<Derived> [重复]
【发布时间】:2017-03-23 14:55:29
【问题描述】:

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# 不是很容易提供这种转换吗(即基类可以完全模仿派生类)?

谢谢

【问题讨论】:

  • 你的例子是错误的,List&lt;Person&gt; Jill 没有属性ClassRoom 它是一个列表...
  • 您如何期望public 字段对派生类不可见?
  • 不,你不能让人表现得像老师,访问一个人对象上只属于老师的属性永远不会工作......我可能会在这里重新考虑你的继承......我认为你不使用继承
  • 如果您不想让 Teach 看到 name 属性,您将不得不创建另一个继承 Person 的类。继承的类总是获取基类的所有属性。
  • Jill 的类型是 List&lt;Person&gt;,而不是 Person,因此它既没有 ClassRoom,也没有 Name。第二:由于Teacher inheritsPerson确实 有一个Name。所以有点不清楚你在问什么。第三:List&lt;Teacher&gt; 不是 List&lt;Person&gt;,所以它是不可分配的。即使在IList&lt;T&gt; 接口中,T 也是不变的,因为它既是输入值也是输出值。

标签: c# list class generics covariance


【解决方案1】:

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


这里有一些问题。 List&lt;Base&gt; 变量不可能引用 List&lt;Derived&gt; 实例。这是因为,例如List&lt;Base&gt; 必须实现将Base 实例添加到List 的操作,而List&lt;Derived&gt; 不能支持这样的操作。

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

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

在某些语言中,您可以通过“私有继承”获得这样的效果——但这不是 C# 中的事情。您可以做的最接近的事情是让School not 派生自Building,但可能包含(私有)Building-typed 字段或属性。那么,当然,根本不可能将School 存储在List&lt;Building&gt; 中。

另一种方法是让 School 使用不返回任何内容的 getter 覆盖 Name 属性,这实际上更接近您的代码 cmets 建议的内容。

【讨论】:

    【解决方案2】:

    根据您的问题的 cmets,您的 List&lt;Building&gt; 中的对象不能任意转换为 School,除非它最初构造为 School 对象(或派生自 School 的类)。考虑一下如果你有另一个名为Hospital 的类派生自Building 并且有自己的属性比如NumberOfPatients 并且这是该列表中的对象之一会发生什么。您只是不能将 Hospital 实例转换为 School 并调用仅在 School 上可用的属性 - 这样做是没有意义的。

    也就是说,您仍然可以让List&lt;Building&gt; 存储任何类型的Building 对象,包括派生对象(这样您就有一个包含BuildingSchoolHospital 对象的混合列表)。然后,您可以通过 Linq OfType 获取特定派生类型的对象的枚举(添加到文件顶部 using System.Linq;)。所以像buildingList.OfType&lt;School&gt;() 这样的东西会给你一个只有School 对象的枚举,你现在可以遍历并调用仅在School 上可用的方法/属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多