2

I have an object hierarchy

public class MyBase {}
public class MyDerived : MyBase {}

and have

List<MyBase> myList;

that is actually filled with instances of MyDerived

In order to access that list as a List<MyDerived>, I'm doing the following:

myList.Cast<MyDerived>().ToList()

I read the MSDN docs on Enumerable.Cast<T>, but it's not clear to me whether the Cast<T> and ToList operations make a new copy of the objects in memory, or simply allow the compiler to access the existing objects as if they were a List<MyDerived>.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
Eric J.
  • 147,927
  • 63
  • 340
  • 553

3 Answers3

5

As its name implies, Cast<T>() just casts objects:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { 
    IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
    if (typedSource != null) return typedSource; 
    if (source == null) throw Error.ArgumentNull("source");
    return CastIterator<TResult>(source);
}

static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
    foreach (object obj in source) yield return (TResult)obj; 
} 

In .Net, it is fundamentally impossible to copy an arbitrary object. It would make no sense for Cast<T>() to copy things.

Note that if T is a value type, Cast<T>() will copy the structs; value types are always copied. (except when passed as ref parameters)

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Using ToList() does creates a copy, or doesn't? – Alireza Sabouri Feb 08 '12 at 18:10
  • @sos00: Wrong. It is impossible to copy an object. `ToList()` makes a copy of the _list_, but it still refers to the same objects. – SLaks Feb 08 '12 at 18:11
  • It might be useful to mention the converse of your own comment here: If one were to implement this code exactly like this _inside_ the assembly containing the type operated on, the behaviour may be different from what the standard extension does: `(TResult)obj` may invoke a user-defined conversion operator (e.g. `class X { public static implicit operator Y() { ... }`) – sehe Feb 08 '12 at 18:11
  • @sehe: Still wrong. Generics are done at runtime; a cast to or from a generic type will _never_ invoke a user-defined operator. – SLaks Feb 08 '12 at 18:13
  • Sigh. Where can I find this information? By the way, this also implies that a cast to a non-generic type may still invoke a userdefined conversion? E.g. `List xs; var convertedtoYs = xs.Cast();` ? You see, there is _nothing generic_ about X nor Y – sehe Feb 08 '12 at 18:53
  • The point that confused me was the MSDN documentation on casting in C# that describes casting an int to a double (value types) which stated that the int is copied. Makes sense that arbitrary objects are not copied. – Eric J. Feb 08 '12 at 18:57
  • @sehe: You're right; I meant to say generic type _parameter_. You can create user-defined conversions between _concrete_ generic types, and cast using them just like concrete non-generic types. However, user-defined conversions cannot be generic or involve generic type parameters. – SLaks Feb 08 '12 at 18:58
  • @EricJ.: Yes. Casting refers to three very different things: Built-in language conversions (eg, `int` to `double`), user-defined conversions, and runtime type-casts. Only the third is _selected_ at runtime. Eric Lippert does a good job explaining these things; I don't have links offhand. – SLaks Feb 08 '12 at 19:00
  • @sehe: `Where can I find this information?` That's a good question. I amalgamated this knowledge from years of personal experience and from reading Eric Lippert. It is most important to _understand_ how it works, by logically extrapolating from specifications and behavior. Programming is all about intense logical thought. – SLaks Feb 08 '12 at 19:02
  • See http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx for a good description. – SLaks Feb 08 '12 at 19:02
  • @SLaks: great. Rephrasing the way I finally understood it: 'convertibility' is not available as a generic _argument type_ constraint, therefore `void F(TX x) { return (TY) x; }` will never compile. It took me a while because I do too much C++ lately :) – sehe Feb 08 '12 at 19:21
  • @SLaks that link, while good reading, purposely avoids the issue of generics. – sq33G Feb 08 '12 at 19:22
  • @sehe: Yes. This is one of the primary differences between generics and templates. I, coming from the opposite side, am just barely aware of the neat tricks you can do with templates. – SLaks Feb 08 '12 at 19:25
0

No

Unsurprisingly (!) it Casts them.

I read the MSDN docs on Enumerable.Cast, but it's not clear to me whether the Cast and ToList operations make a new copy of the objects in memory, or simply allow the compiler to access the existing objects as if they were a List.

Yes, specifically this scenario will allow the compiler to access the existing objects as if they were a List<MyDerived>

sehe
  • 374,641
  • 47
  • 450
  • 633
  • User-defined conversions are purely a compile-time feature; no runtime method ever uses them. – SLaks Feb 08 '12 at 18:07
  • @SLaks: thanks again. I learned something today. And you saved me the effort of trying it out :) – sehe Feb 08 '12 at 18:09
0

The cast call:

enumerable.Cast<T>();

is equivalent to:

enumerable.Select(x=>(T)x)

And:

enumerable.ToList<T>()

is equivalent to:

List<T> myList = new List<T>();
foreach(T item in enumerable)
{
   myList.Add(item);
}

so...

enumerable.Cast<T2>.ToList()

is equivalent to:

List<T2> myList = new List<T2>();
foreach(T item in enumerable)
{
   myList.Add((T2)item);
}

Both of which will promptly blow up if enumerable contains any entries that are not of a type castable to T2.

It would be safer as

  enumerable.Where(x=>x is T2).Cast<T2>().ToList();

or

  enumerable.OfType<T2>().ToList();

However, none of these copies the objects contained in the original collection. They just create collections containing references to the original objects.

Larry Hector
  • 183
  • 1
  • 11