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

Cast Collection<Derived> to Collection<Base>

这是一个经常被问到的问题。你想要的特征的名字是泛型协方差;也就是说,如果长颈鹿是一种动物,那么长颈鹿的列表就是一种动物列表

问题是长颈鹿的列表不是一种动物列表。你可以把老虎放到动物列表中,但你不能把老虎放到长颈鹿列表中,因此长颈鹿列表不能用于任何需要动物列表的上下文中。

您应该使用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());
}