这里有另一个简单的问题,就是在逃避我。
我有2个班级:
namespace Assets
{
public class BaseAsset
{
// Code here
}
}
并且
namespace Assets
{
public class Asset : BaseAsset
{
// Code here
}
}
我有一个从数据库返回Asset集合的函数,我希望另一个函数执行该函数并返回一个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
答案 0 :(得分:10)
这是一个非常常见的问题。您需要的功能的名称是通用协方差;也就是说,“如果长颈鹿是一种动物,那么长颈鹿列表就是一种动物名单。”
问题是长颈鹿的名单不是一种动物名单。你可以将一只老虎放入动物名单中,但是你不能将老虎放入长颈鹿名单中,因此长颈鹿名单不能用于预计会有动物名单的任何情况。
您应该使用IEnumerable<T>
而不是Collection<T>
的原因是因为从C#4开始,IEnumerable<T>
在T中是协变的,前提是提供的类型参数都是引用类型。也就是说,字符串序列可以用作对象序列,因为它们都是引用类型。但是一系列int不能用作对象序列,因为一个是值类型。
这是安全的原因是因为无法将老虎插入IEnumerable<Giraffe>
。
答案 1 :(得分:2)
如果您想要.ToList
的简易性,只需编写自己的.ToCollection
扩展方法即可。实现应该是直截了当的 - 采用IEnumerable<T>
,循环遍历它并将所有内容添加到Add
的集合中。
答案 2 :(得分:1)
问题是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>
或{{ 1}})指定返回值和函数参数时。
答案 3 :(得分:0)
为什么不提取界面并使用它,而不是尝试强制转换为基类。
答案 4 :(得分:0)
由于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());
}