tags:

views:

853

answers:

7

I have a

List<InputField>

but I need a

List<IDataField>

Is there a way to cast this in c#? Or use Linq to get same result?

I have two classes that implement the same interface:

interface IDataField { }
class InputField : IDataField { }
class PurchaseField : IDataField { }

This List comes from a Linq-to-Sql query:

List<InputField> list = (from i .... select i).ToList();
+8  A: 
List<InputField> raw = (from i .... select i).ToList();
List<IDataField> result = raw.OfType<IDataField>().ToList();
David B
A: 

I don't know that a direct cast would have the desired effect. The rare few times I've done this, it's usually something like:

List<InputField> list = .....
List<IDataField> list2 = new (List<IDataField>((IDataField[])list.ToArray()));
DannySmurf
+2  A: 

Since the list is coming from

List<InputField> list = (from i .... select i).ToList();

Couldn't you just fix the "select i" part to instead return IDataField instead? Something like this:

List<InputField> list = (from i .... select (IDataField)i).ToList();

If that doesn't work, perhaps the "Cast" extension to IEnumerable will work:

List<DataField> list2 = list.Cast<IDataField>();
Arjan Einbu
Actually fixing the "select" part of query would be the best. Otherwise look at Helen Toomik's answer for the difference between OfType and Cast...
Arjan Einbu
+2  A: 

You could also use List.ConvertAll.

Documentation: http://msdn.microsoft.com/en-us/library/73fe8cwf.aspx

Example:

List<IDataField> newList = oldList.ConvertAll(i => i as IDataField);
CubanX
Just what I was going to say. Note that in C# 2.0 you'd need to specify the generic explicitly, but in C# 3.0 (as above) it is inferred.
Marc Gravell
+17  A: 

Both .OfType<T> and .Cast<T> will return a list of T, but the meaning of the two methods is different.

list.OfType filters the original list and returns all items which are of type T, and skips the ones that are not of that type.

list.Cast casts all items in the original list to type T, and throws an exception for items which cannot be cast to that type.

In your case both would give the same result, but using .Cast would communicate your intent a lot more clearly, so I would recommend using that.

List<InputField> list = (from i .... select i).Cast<IDataField>.ToList();
Helen Toomik
Damn - beat me by 35 seconds. Upvoted - this is the correct answer as the OP is looking for a cast.
Erik Forbes
I accepted this answer because I was indeed looking for a cast. Thanks to everyone.
bentford
+2  A: 

Just in case: I've little C# experience, but if this generic construction means the same thing it does in Java, then you must create a whole new List parametrized by the supertype. In other words, if every instance of Bangle is also an instance of Akthud, it does not follow that every List<Bangle> is also a List<Akthud>.

The reason for this is that you can have two references to this List<Bangle>. If the second reference casts and then refers to it as a List<Akthud>, then it is permitted to add an Akthud to it - but now the first reference has a List<Bangle> whose members are not all Bangles. Violation!

That being said, David B's answer should indeed do what you want, correctly, AFAICT. (It looks like a copy operation.)

[And if I'm misunderstanding the semantics of C# generics, I hope someone corrects me in a comment!]

Paul Brinkley
You're right - generics in C# are neither co-variant nor contra-variant; something they're fixing in C# 4. Here's a post on MSDN explaining this: http://msdn.microsoft.com/en-us/library/ms228359(VS.80).aspx
Erik Forbes
Nice link. I think you meant this as an illustration of the generic variance issue, rather than of the C# 4 change (I saw nothing there about that). Just to be clear(?). Still very informative, however.
Paul Brinkley
A: 

ConvertAll seems like a better solution, since it doesn't depend on Linq library and is shorter and more intuitive.

But both of them are rather workarounds, than solutions. Both of them create a new collection with the same data. There should be a support for generic covariance in .net, i.e. upcasting in the generic collections, which would allow you to do that naturally. For example:

List<IDataField> ls = (List<IDataField>)List<InputField>

From my search so far, my conclusion, is that .Net as of 3.5 doesn't support this feature. Hence we have to end up with workarounds.

These are the discussions on the topic:

husayt