Cast Collection<Derived> to Collection<Base>
本文关键字:gt lt Collection Base to Derived Cast | 更新日期: 2023-09-27 17:50:03
我又有一个简单的问题。
我有两个类:
namespace Assets
{
public class BaseAsset
{
// Code here
}
}
和
namespace Assets
{
public class Asset : BaseAsset
{
// Code here
}
}
我有一个函数,从数据库返回一个集合的资产,我想要另一个函数来执行该函数,并返回一个集合的BaseAsset。我试过了:
public static Collection<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return (Collection<BaseAsset>)AssetData.getAssets(CategoryId, UserId, CompanyId);
}
但是你可以猜到,它不起作用。如果我在处理列表,我可以这样做:
public static List<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return AssetData.getAssets(CategoryId, UserId, CompanyId).Cast<BaseAsset>().ToList();
}
但是我更喜欢使用集合,有人能想出一个优雅的解决方案吗?
欢呼,r3plica
这是一个经常被问到的问题。你想要的特征的名字是泛型协方差;也就是说,如果长颈鹿是一种动物,那么长颈鹿的列表就是一种动物列表
问题是长颈鹿的列表不是一种动物列表。你可以把老虎放到动物列表中,但你不能把老虎放到长颈鹿列表中,因此长颈鹿列表不能用于任何需要动物列表的上下文中。
您应该使用IEnumerable<T>
而不是Collection<T>
的原因是因为在c# 4中,IEnumerable<T>
在T中是协变的,前提是提供的类型参数都是引用类型。也就是说,字符串序列可以用作对象序列,因为两者都是引用类型。但是int型序列不能用作对象序列,因为其中一个是值类型。
这是安全的原因是因为没有办法将虎插入IEnumerable<Giraffe>
。
如果您想要.ToList
的易用性,只需编写自己的.ToCollection
扩展方法。实现应该很简单——取一个IEnumerable<T>
,循环遍历它并将所有内容添加到Add
的集合中。
问题是Collection<T>
和ICollection<T>
是不变的(即Collection<BaseAsset>
既不是Collection<Asset>
的子类型也不是超类型)。
返回IEnumerable<BaseAsset>
或IReadOnlyList<BaseAsset>
而不是Collection<BaseAsset>
,这个问题将非常容易解决。
也就是说,你可以这样写:
public static IEnumerable<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return AssetData.getAssets(CategoryId, UserId, CompanyId);
}
不需要强制转换。
一般来说,在指定返回值和函数参数时,应该选择接口类型(如IList<T>
、IReadOnlyList<T>
、ICollection<T>
或IEnumerable<T>
)而不是具体类型(Collection<T>
或List<T>
)。
与其尝试转换为基类,为什么不直接提取一个接口并使用它呢?
由于Collection<T>
类有一个以IList<T>
作为参数的构造函数,因此您总是可以这样做:
Collection<BaseAsset> = new Collection<BaseAsset>(
assetList.Cast<BaseAsset>().ToList());
当然,如果您需要重用这个行为,您可以创建一个CastToCollection扩展:
public static Collection<TResult> CastToCollection<TResult>(this IEnumerable source)
{
return new Collection<TResult>(source.Cast<TResult>().ToList());
}