C# 中的 Cast 删除实现的成员数组
Cast drops Implemented Member array in C#
考虑:
class Order
{
public List<OrderItem> OrderItems {get;set;}
}
class SalesOrder : Order
{
public List<SalesOrderItem> OrderItems {get;set;}
}
class OrderItem {}
class SalesOrderItem : OrderItem {}
尝试投射时
SalesOrder salesorder = new SalesOrder();
salesorder.OrderItems = List<SalesOrderItem>();
salesorder.OrderItems.Add(new SalesOrderItem());
Order order = salesorder;
order.Orderitems
为空。有什么方法可以让这个演员表与派生属性一起使用吗?
我已经尝试过与接口的协变,但是无法使用 List
并且使用 IEnumerable
将不允许 public setter。关于如何实现这一点有什么想法吗?
这就是所谓的“shadowing”。
您的 salesOrder
有两个 OrderItems
属性 - 一个仅对 SalesOrder
可见,一个仅对 Order
可见。子 class 中的 OrderItems
属性 隐藏了父 class 中的 OrderItems
属性。您正在向一个项目添加一个项目,然后观察另一个项目。
我认为您需要重新考虑您的设计,但您可以通过从子项 class:
中删除 属性 来解决此问题
class Order
{
public Order()
{
OrderItems = new List<OrderItem>();
}
public List<OrderItem> OrderItems {get;set;}
}
class SalesOrder : Order { }
class OrderItem {}
class SalesOrderItem : OrderItem {}
另一种选择是使 Order
成为通用的 class:
class Order<T> where T: OrderItem
{
public Order()
{
OrderItems = new List<T>();
}
public List<T> OrderItems {get;set;}
}
class SalesOrder : Order<SalesOrderItem> { }
class OrderItem {}
class SalesOrderItem : OrderItem {}
SalesOrder salesorder = new SalesOrder();
salesorder.OrderItems.Add(new SalesOrderItem());
Order<SalesOrderItem> order = salesorder;
第二种方法的好处是它允许 OrderItems
存储特定类型的订单项而不是最一般的类型。
您的代码中有几处错误。看起来您混淆了一些 OO 概念,以及它们在 C# 中的实现方式。
在您的代码中,您试图从 Order
导出 SalesOrder
。这是最重要的错误:它违反了 Liskov substitution principle。特别是,这意味着派生 class 可能不会向其成员的签名添加额外的约束。请注意,您的 SalesOrder
class 确实 添加了这样一个约束:订单商品的类型必须为 SalesOrderItem
,而 Order
class 接受来自 OrderItem
.
的所有订单项目
您想要的是专业化:SalesOrder
class 比 Order
class 更严格:它只能包含 SalesOrderItems。专业化是通过使用泛型来实现的。所以你可以定义你的 classes 如下:
public class Order<T> where T: OrderItem
{
public List<T> OrderItems { get; set; }
}
public class OrderItem
{
}
public class SalesOrder : Order<SalesOrderItem>
{
}
public class SalesOrderItem : OrderItem
{
}
更新:为了说明为什么无法将SalesOrder
转换为Order<OrderItem>
,请考虑以下静态方法:
public static void AddOrderItem(Order<OrderItem> order)
{
var item = new OrderItem();
order.OrderItems.Add(order);
}
如果 SalesOrder
是从 Order<OrderItem>
派生的,你可以这样写:
var order = new SalesOrder();
AddOrderItem(order);
但是,这会导致类型错误,因为 SalesOrder 不能包含普通的 OrderItem。现在,如果您将 SalesOrder
转换为 Order
的唯一原因是 读取 orderitems,您可以引入一个接口:
public interface IOrder
{
IEnumerable<OrderItem> GetItems();
}
public class Order<T> : IOrder where T: OrderItem
{
public List<T> OrderItems { get; set; }
public IEnumerable<OrderItem> GetItems()
{
return this.OrderItems;
}
}
现在您可以将 SalesOrder
对象转换为 IOrder
并读取订单项。
考虑:
class Order
{
public List<OrderItem> OrderItems {get;set;}
}
class SalesOrder : Order
{
public List<SalesOrderItem> OrderItems {get;set;}
}
class OrderItem {}
class SalesOrderItem : OrderItem {}
尝试投射时
SalesOrder salesorder = new SalesOrder();
salesorder.OrderItems = List<SalesOrderItem>();
salesorder.OrderItems.Add(new SalesOrderItem());
Order order = salesorder;
order.Orderitems
为空。有什么方法可以让这个演员表与派生属性一起使用吗?
我已经尝试过与接口的协变,但是无法使用 List
并且使用 IEnumerable
将不允许 public setter。关于如何实现这一点有什么想法吗?
这就是所谓的“shadowing”。
您的 salesOrder
有两个 OrderItems
属性 - 一个仅对 SalesOrder
可见,一个仅对 Order
可见。子 class 中的 OrderItems
属性 隐藏了父 class 中的 OrderItems
属性。您正在向一个项目添加一个项目,然后观察另一个项目。
我认为您需要重新考虑您的设计,但您可以通过从子项 class:
中删除 属性 来解决此问题class Order
{
public Order()
{
OrderItems = new List<OrderItem>();
}
public List<OrderItem> OrderItems {get;set;}
}
class SalesOrder : Order { }
class OrderItem {}
class SalesOrderItem : OrderItem {}
另一种选择是使 Order
成为通用的 class:
class Order<T> where T: OrderItem
{
public Order()
{
OrderItems = new List<T>();
}
public List<T> OrderItems {get;set;}
}
class SalesOrder : Order<SalesOrderItem> { }
class OrderItem {}
class SalesOrderItem : OrderItem {}
SalesOrder salesorder = new SalesOrder();
salesorder.OrderItems.Add(new SalesOrderItem());
Order<SalesOrderItem> order = salesorder;
第二种方法的好处是它允许 OrderItems
存储特定类型的订单项而不是最一般的类型。
您的代码中有几处错误。看起来您混淆了一些 OO 概念,以及它们在 C# 中的实现方式。
在您的代码中,您试图从 Order
导出 SalesOrder
。这是最重要的错误:它违反了 Liskov substitution principle。特别是,这意味着派生 class 可能不会向其成员的签名添加额外的约束。请注意,您的 SalesOrder
class 确实 添加了这样一个约束:订单商品的类型必须为 SalesOrderItem
,而 Order
class 接受来自 OrderItem
.
您想要的是专业化:SalesOrder
class 比 Order
class 更严格:它只能包含 SalesOrderItems。专业化是通过使用泛型来实现的。所以你可以定义你的 classes 如下:
public class Order<T> where T: OrderItem
{
public List<T> OrderItems { get; set; }
}
public class OrderItem
{
}
public class SalesOrder : Order<SalesOrderItem>
{
}
public class SalesOrderItem : OrderItem
{
}
更新:为了说明为什么无法将SalesOrder
转换为Order<OrderItem>
,请考虑以下静态方法:
public static void AddOrderItem(Order<OrderItem> order)
{
var item = new OrderItem();
order.OrderItems.Add(order);
}
如果 SalesOrder
是从 Order<OrderItem>
派生的,你可以这样写:
var order = new SalesOrder();
AddOrderItem(order);
但是,这会导致类型错误,因为 SalesOrder 不能包含普通的 OrderItem。现在,如果您将 SalesOrder
转换为 Order
的唯一原因是 读取 orderitems,您可以引入一个接口:
public interface IOrder
{
IEnumerable<OrderItem> GetItems();
}
public class Order<T> : IOrder where T: OrderItem
{
public List<T> OrderItems { get; set; }
public IEnumerable<OrderItem> GetItems()
{
return this.OrderItems;
}
}
现在您可以将 SalesOrder
对象转换为 IOrder
并读取订单项。