If I have these two classes:
class A {}
class B : A {}
and I make a List<A> but I want to add a List<B> to it by calling List<A>.AddRange(List<B>) but the compiler refuses:
Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>
which I completely understand because IEnumerable<B> does not inherit from IEnumerable<A>, its generic type has the inheritance.
My solution is to enumerate through List<B> and individually add items because List<A>.Add(A item) will work with B items:
foreach(B item in listOfBItems)
{
listOfAItems.Add(item);
}
However, that's rather non-expressive because what I want is just AddRange.
I could use
List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});
but that's unnecessarily convoluted and a misnomer because I'm not converting, I'm casting .
Question: If I were to write my own List-like collection what method would I add to it that would allow me to copy a collection of B's into a collection of A's as a one-liner akin to List<A>.AddRange(List<B>) and retain maximum type-safety. (And by maximum I mean that the argument is both a collection and type inhertance checking.)
Indeed, generic types are not variant right now. In C# 4.0, IEnumerable<B> will be convertible to IEnumerable<A> if B is convertible to A via a reference conversion. For some details on the design of this feature, see:
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
This does unfortnuately not work because generics in .net do not (yet) support covariance.
You can make a small helper method or class to overcome this issue however.
If you implement your own list class, you can add covariance using an additional generic parameter:
class MyList<T> {
void AddRange<U>(IEnumerable<U> items) where U: T {
foreach (U item in items) {
Add(item);
}
}
}
Can't you just do:
listOfAItems.AddRange(listOfBItems.Cast<A>());
I was able to achieve this using LINQ...
listOfAItems.AddRange(listOfBItems.Cast<A>());
In case you find yourself in a situation where generic types are not variant, the following extension method can make your life easier:
public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
foreach(TOther e in collection) {
list.Add(e);
}
}
Instead of having to derive from List<T> or having this method in some utility class, using it as an extension method simplifies usage. You can also profit from inference, so this formerly invalid call will become valid without any modification:
List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);
The only thing I can come up with is this
public class MyList<T> : List<T>
{
public void AddRange<Tother>(IEnumerable<Tother> col)
where Tother: T
{
foreach (Tother item in col)
{
this.Add(item);
}
}
}
Calling it means doing MyList<A>.AddRange<B>(MyList<B>). This fails if the argument is not enumerable or if the type inheritance doesn't work out so it satisfies my question's maximum type safety requirement.
Related
I have some custom list which is based on IEnumerable<T>. Now I want to overload the constructor and do some custom stuff with the initial list I get.
//Constructor
public CustomList(IEnumerable<T> collection) : base(collection)
{
//do some stuff with the collection e.g. iterate
foreach(T obj in collection)
{
//do some stuff with obj
}
}
Is it even possible to do so? Because IEnumerable can only be enumerated once at base(collection), but the loop will fail, I guess.
EDIT:
since it is not completely clear for everyone: My base class is IEnumerable<T> and IEnumerable can only be enumerated once in my case.
Assuming your base class is List<T>, then the base constructor adds all the items to the current object. So you could do the following as often as you want:
foreach (T obj in this) { }
But I've heard it's a bad idea to inherit from List<T>, that you should implement IList<T> instead, even if it is just a wrapper around List<T>. I just can't remember why that is.
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
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>.
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.
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.
Instead of trying to cast to the base class, why not just extract an interface and use that.
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());
}
I had to implement 2 interface same time with different generic parameter as below. I get confused enough about it. I had no idea which one of them iterate itself in foreach. Now i understand first one is implicitly choosen.
I have tried new BarList().GetEnumerator() but i can not specify type parameter on method level.
Only solution i have found it that casting it to interface like(new BarList() as IEnumerable<string>)
After confusing about it enough. I just wanted to know that this design is not really good idea ? I have to avoid to implement same generic interface one more time ?
class Program
{
static void Main(string[] args)
{
foreach (var item in new BarList())
{
}
}
}
class BarList: IEnumerable<string>, IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
throw new NotImplementedException();
}
}
Edit:
Let me explain why i am going in this way.
I had to Implement IPagedList<T> interface which is inherited from IList<T>. I wanted to write extension method which convert it to My view model. like below
GetAll().ToPagedList(pageindex);//which is returning IPagedList Then i wanted to use it like below;
GetAll().ToPagedList(pageindex).ToViewModel<T,TViewModel>();
For achieve this I tried to return IPagedList<ViewModel> by that extension method.In that case I have to implement IPagedList 2 times with different parameter. But this strategy made confusing things. This is reason of it.
This seems a bit confusing. Why not make it explicit what is happening by adding the enumerators as properties rather than implementing them on the class. For example,
class ProductCollection
{
public IEnumerable<int> EnumerateTheInts { get { //code to produce enumerator }}
public IEnumerable<string> EnumerateTheStringss { get { //code to produce enumerator }}
}
It isn't always bad to implement an open generic interface twice on an object. For example, IHandle could be implemented by a class which can handle two types of T. However, I would find it confusing to implement IEnumerable twice, because you might not enumerate the type you expect in a for-each or in LINQ. Same reasoning for implementing more than one indexer incidentally. The type of your indexer will determine your result, which I can testify to being extremely confusing!
The compiler is picking the IEnumerator<int> GetEnumerator method by following the rules in 8.8.4 of the C# language specification which first looks for an accessible GetEnumerator() method on the BarList type. The only one of those which is available is the one returning IEnumerator<int>.
If you had made that method use explicit interface implementation as well, then it would have gone onto the later stages of section 8.8.4, which states that if there is more than one type T such that there is an implicit conversion from the expression type (BarList here) to IEnumerable<T> then an error is produced.
I would say this is a confusing design - I would probably add properties or methods to retrieve appropriate "views" on the data.
I'd avoid it. However, it depends on your usage.
It will be okay if you just wanted to pass the instance into a function that expects a IEnumerable<string> parameter explicitely:
you won't have to cast
the function won't even 'see' the other interfaces implemented, so there isn't any confusion.
YMMV
Your current design is confusing. While you have not provided any information about the nature of the collection itself, from the name, I can assume you are supposed to iterate over a bunch of products. Perhaps, you should simply have a class of type Product with a string property and an int property and simply return an IEnumerable<Product> instead.
This way, with LINQ extension methods, you can compose the IEnumerable<T> object you actually mean with:
collection.Select(product => product.IntegerProperty)
collection.Select(product => product.StringProperty)
Of course, you can provide helper methods inside the object as well:
class ProductCollection : IEnumerable<Product> {
public IEnumerable<Product> GetEnumerator() {
// ... return Product objects here.
}
public IEnumerable<int> AsIntegerCollection() {
// yield the integer collection here
}
public IEnumerable<string> AsStringCollection() {
// yield the string collection here
}
}
What are these collections of string and ints? I suppose they mean something in relation with the Product (for example Name, Id, etc...) so I would rather do something like this:
class ProductCollection : IEnumerable<Product>
{
public IEnumerator<Product> GetEnumerator()
{
...
}
public IEnumerator<string> ProductNames // a helper to enumerate product names
{
...
}
public IEnumerator<int> ProductIds // a helper to enumerate product ids
{
...
}
}
I have some code like this:
public class EffectValues : IEnumerable<object>
{
public object [ ] Values { get; set; }
public IEnumerator<object> GetEnumerator ( )
{
return this.Values.GetEnumerator ( );
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ( )
{
return this.GetEnumerator ( );
}
}
But the compiler complains saying:
"Cannot implicitly convert type
'System.Collections.IEnumerator' to
'System.Collections.Generic.IEnumerator'.
An explicit conversion exists (are you
missing a cast?)"
I thought the Array type implemented both IEnumerable interfaces, does it not? Because I can use Linq features on the Values instance directly.
This is a subtle and a bit unfortunate. The easy workaround is:
public IEnumerator<object> GetEnumerator ( )
{
return ((IEnumerable<object>)this.Values).GetEnumerator ( );
}
I thought the Array type implemented both IEnumerable interfaces, does it not?
The rules are:
System.Array implements IEnumerable "implicitly", with public methods.
every array type T[] inherits from System.Array.
every array type T[] implements IList<T>, IEnumerable<T> and so on.
therefore every array type T[] is convertible to IEnumerable<T>
Notice that the third point was NOT
every array type T[] implements IList<T>, IEnumerable<T> and so on with public methods and properties defined on T[] that implicitly implement the members
And there you go. When you look up GetEnumerator, we look it up on object[] and don't find it, because object[] implements IEnumerable<object> explicitly. It is convertible to IEnumerable<object>, and convertibility doesn't count for lookups. (You wouldn't expect a method of "double" to appear on int just because int is convertible to double.) We then look at the base type, and find that System.Array implements IEnumerable with a public method, so we've found our GetEnumerator.
That is, think about it like this:
namespace System
{
abstract class Array : IEnumerable
{
public IEnumerator GetEnumerator() { ... }
...
}
}
class object[] : System.Array, IList<object>, IEnumerable<object>
{
IEnumerator<object> IEnumerable<object>.GetEnumerator() { ... }
int IList<object>.Count { get { ... } }
...
}
When you call GetEnumerator on object[], we don't see the implementation that is an explicit interface implementation, so we go to the base class, which does have one visible.
How do all the object[], int[], string[], SomeType[] classes get generated "on the fly"?
Magic!
This is not generics, right?
Right. Arrays are very special types and they are baked in at a deep level into the CLR type system. Though they are very similar to generics in a lot of ways.
It seems like this class object [] : System.Array is something that can't be implemented by a user, right?
Right, that was just to illustrate how to think about it.
Which one do you think is better: Casting the GetEnumerator() to IEnumerable<object>, or just use foreach and yield?
The question is ill-formed. You don't cast the GetEnumerator to IEnumerable<object>. You either cast the array to IEnumerable<object> or you cast the GetEnumerator to IEnumerator<object>.
I would probably cast Values to IEnumerable<object> and call GetEnumerator on it.
I will probably use casting but I am wondering if this is a place where you or some programmer who could read the code, would think it's less clear.
I think it's pretty clear with the cast.
when you said implicit implementation, you mean in the form of Interface.Method, right?
No, the opposite:
interface IFoo { void One(); void Two(); }
class C : IFoo
{
public void One() {} // implicitly implements IFoo.One
void IFoo.Two() {} // explicitly implements IFoo.Two
}
The first declaration silently implements the method. The second is explicit about what interface method it implements.
What's the reason for implementing IEnumerable<T> like that, instead of implicit implementation with public methods? I got curious because you said "This is a subtle and a bit unfortunate", so it seems like it's because of an older decision that forced you to do this I imagine?
I don't know who made this decision. It is kind of unfortunate though. It's confused at least one user -- you -- and it confused me for a few minutes there too!
I would have thought the Array type would be something like this: public class Array<T> : IEnumerable<T> etc. But instead there is some magical code about it then, right?
Right. As you noted in your question yesterday, things would have been a lot different if we'd had generics in CLR v1.
Arrays are essentially a generic collection type. Because they were created in a type system that did not have generics, there has to be lots of special code in the type system to handle them.
Next time you design a type system put generics in v1 and make sure you get strong collection types, nullable types and non-nullable types baked in to the framework from the beginning. Adding generics and nullable value types post hoc was difficult.
You have to cast the array to IEnumerable<object> to be able to access the generic enumerator:
public IEnumerator<object> GetEnumerator() {
return ((IEnumerable<object>)this.Values).GetEnumerator();
}
How do I get the type of a generic typed class within the class?
An example:
I build a generic typed collection implementing ICollection< T>. Within I have methods like
public void Add(T item){
...
}
public void Add(IEnumerable<T> enumItems){
...
}
How can I ask within the method for the given type T?
The reason for my question is: If object is used as T the collection uses Add(object item) instead of Add(IEnumerable<object> enumItems) even if the parameter is IEnumerable. So in the first case it would add the whole enumerable collection as one object instead of multiple objects of the enumerable collection.
So i need something like
if (T is object) {
// Check for IEnumerable
}
but of course that cannot work in C#. Suggestions?
Thank you very much!
Michael
You can use: typeof(T)
if (typeof(T) == typeof(object) ) {
// Check for IEnumerable
}
Personally, I would side step the issue by renaming the IEnumerable<T> method to AddRange. This avoids such issues, and is consistent with existing APIs such as List<T>.AddRange.
It also keeps things clean when the T you want to add implements IEnumerable<T> (rare, I'll admit).
If you want to use the is operator in a generic class/method you have to limit T to a reference type:
public void MyMethod<T>(T theItem) where T : class
{
if (theItem is IEnumerable) { DoStuff(); }
}