Reputation: 13387
Got another simple question here that is eluding me.
I have 2 classes:
namespace Assets
{
public class BaseAsset
{
// Code here
}
}
And
namespace Assets
{
public class Asset : BaseAsset
{
// Code here
}
}
I have a function that returns a collection of Asset from the database and I want another function to execute that function and return a collection of BaseAsset. I have tried this:
public static Collection<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return (Collection<BaseAsset>)AssetData.getAssets(CategoryId, UserId, CompanyId);
}
but as you can guess, it doesn't work. If I was working with lists, I could do:
public static List<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return AssetData.getAssets(CategoryId, UserId, CompanyId).Cast<BaseAsset>().ToList();
}
But I would prefer to use a collection, can anyone come up with an elegant solution?
Cheers, r3plica
Upvotes: 2
Views: 1831
Reputation: 30902
Since the Collection<T>
class has a constructor that takes an IList<T>
as an argument, you can always do:
Collection<BaseAsset> = new Collection<BaseAsset>(
assetList.Cast<BaseAsset>().ToList());
Of course, if you need to reuse this behaviour, you could make a CastToCollection extension:
public static Collection<TResult> CastToCollection<TResult>(this IEnumerable source)
{
return new Collection<TResult>(source.Cast<TResult>().ToList());
}
Upvotes: 0
Reputation: 660289
This is a very frequently asked question. The name of the feature that you want is generic covariance; that is, the feature that says "if a giraffe is a kind of animal then a list of giraffes is a kind of list of animals."
The problem is that a list of giraffes is not a kind of list of animals. You can put a tiger into a list of animals, but you can't put a tiger into a list of giraffes, and therefore a list of giraffes cannot be used in any context where a list of animals is expected.
The reason you should use IEnumerable<T>
instead of Collection<T>
is because as of C# 4, IEnumerable<T>
is covariant in T, provided that the type arguments provided are both reference types. That is, a sequence of strings can be used as a sequence of objects, because both are reference types. But a sequence of ints cannot be used as a sequence of objects, because one is a value type.
The reason this is safe is because there is no way to insert a tiger into an IEnumerable<Giraffe>
.
Upvotes: 11
Reputation: 16796
The problem is that Collection<T>
and ICollection<T>
are invariant (that is, Collection<BaseAsset>
is neither a subtype nor a supertype of Collection<Asset>
).
The problem will be very easily solved by returning either IEnumerable<BaseAsset>
or IReadOnlyList<BaseAsset>
instead of Collection<BaseAsset>
.
That is, you can write:
public static IEnumerable<BaseAsset> GetCategoryAssets(int CategoryId, string UserId, string CompanyId)
{
return AssetData.getAssets(CategoryId, UserId, CompanyId);
}
The cast becomes unnecessary.
In general, you should prefer interface types (such as IList<T>
, IReadOnlyList<T>
, ICollection<T>
or IEnumerable<T>
) over concrete types (Collection<T>
or List<T>
) when specifying return values and function parameters.
Upvotes: 2
Reputation: 3910
Instead of trying to cast to the base class, why not just extract an interface and use that.
Upvotes: 0
Reputation: 65156
If you want the ease of .ToList
, just write your own .ToCollection
extension method. The implementation should be straightforward - take an IEnumerable<T>
, loop through it and add everything into a collection with Add
.
Upvotes: 2