Once again, in this series of posts I look at the parts of the. NET Framework, which may seem trivial, but can help improve Your code by making it easier to write and maintain. The index of all my past little wonders posts can is found here.
We ' ve seen how the Select () extension method lets you project a sequence from one type to a new type which is Han Dy for getting just parts of items, or building new items. But what happens when the items in the sequence was already the type you want, but the sequence itself was typed to an inte Rface or Super-type instead of the sub-type you need?
For example, the sequence of Rectangle stored in an ienumerable<shape> and want to cons Ider it an ienumerable<rectangle> sequence instead.
Today we ' ll look at both handy extension methods, cast<tresult> () and oftype<tresult> () whic H help you with the this task.
Cast<tresult> () –attempt to cast all items to type TResult
So, the first thing we can do would is to attempt to create a sequence of TResult from every item in the source s Equence. Typically we ' d do the If we had an ienumerable<t>where we knew the every item was actually a tresult< /c7> where TResult inherits/implements T.
For example, assume the typical Shape example classes:
1://abstract base class
2:public abstract class Shape {}
4://A basic rectangle
5:public class Rectangle:shape
6: {
7: Public int Widtgh {get; set;}
8: Public int Height {get; set;}
9:}
And let's assume we have a sequence of shape where every shape is a Rectangle...
1:var shapes = new list<shape>
2: {
3: new Rectangle {Width = 3, Height = 5},
4: new Rectangle {Width = ten, Height = 13},
5:
6:};
To get the sequence of Shape as a sequence of Rectangle, of course, we could use a Select () CLA Use, such as:
1://Select each Shape, cast it to Rectangle
2:var rectangles = Shapes
3: . Select (s = = (Rectangle) s)
4: . ToList ();
But that's a bit verbose, and fortunately there is already a facility built in and the form of the Cas T<tresult> () extension method:
1://cast each item to Rectangle and store in a list<rectangle>
2:var rectangles = Shapes
3: . Cast<rectangle> ()
4: . ToList ();
However, we should note that if anything in the list cannot is cast to a Rectangle, you'll get an InvalidCastException thrown at runtime. Thus, if our Shape sequence had a Circle in it, the call to cast<rectangle> () would has Failed. As such, you should if you have reasonably sure of what the sequence actually contains (or is willing to ha Ndle an exception if you ' re wrong).
Another handy use of cast<tresult> () is using the it to convert a IEnumerable to an IENUMERABLE&L T T>. If you are in the signature, you'll see that the cast<tresult> () extension method actually extends the old Er, object-based IEnumerable interface instead of the newer, generic ienumerable<t>.
This was your gateway method for being able to use LINQ on older, non-generic sequences. For example, consider the following:
1://The older, non-generic collections is sequence of object
2:var shapes = new ArrayList
3: {
4: new Rectangle {Width = 3, Height = 13},
5: new Rectangle {Width = ten, Height = 20},
6: //...
7:};
Since This is a older, object based collection, we cannot use the LINQ extension methods on it directly. For example, if I wanted to query the Shape sequence for only those rectangles whose width > is > 5, I can ' t does this:
1://Compiler Error, Where () operates on Ienumerable<t>, not IEnumerable
2:var bigrectangles = Shapes. Where (r = r.width > 5);
However, I can use cast<rectangle> () to treat my ArrayList as an ienumerable<rectangle> and then do the query!
1://Ah, that ' s better!
2:var bigrectangles = Shapes. Cast (). Where (r = r.width > 5);
Or, if you prefer, in LINQ query expression syntax:
2:
3: select S;
One quick warning: cast<tresult> () only attempts to Cast, it won ' t perform a Cast convers Ion. That's, consider this:
1:var intlist = new List<int> {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
3://Casting ints to longs, this should work, right?
4:var Aslong = intlist.cast<long> (). ToList ();
Would the code above work?
No, you ' ll get a InvalidCastException. Remember that cast<tresult> () is an extension of IEnumerable, thus it is a sequence of object< /c5>, which means that it'll box every int as an object as it enumerates over it, and there is no cast Conversion from object to long, and thus the cast fails.
In other words, a cast from int to long would succeed because there is a conversion from int to long. but a cast from int to object to long won't, because you can only unbox an item by cast ing it to its exact type.
For more information in why cast-converting boxed values doesn ' t work, see this post on the dangers of Casting boxed Value S (here).
Oftype<tresult> () –filter sequence to only items of type TResult
So, we've seen how we can use cast<tresult> () to change the type of our sequence, when we expect all the IT EMS of the sequence to is of a specific type. But what does we do when a sequence contains many different types, and we is only concerned with a subset of a given type?
For example, what if a sequence of Shape contains Rectangle and Circle instances, and we just W Ant to select all of the Rectangle instances? Well, let's say we had this sequence of Shape:
1:var shapes = new list<shape>
2: {
3: new Rectangle {Width = 3, Height = 5},
4: new Rectangle {Width = ten, Height = 13},
5: new Circle {Radius = 10},
6: new Square {Side = 13},
7: //...
8:};
Well, we could get the rectangles using Select (), like:
1:var onlyrectangles = Shapes. Where (s = S is Rectangle). ToList ();
But fortunately, an easier to have already been written for us in the form of the oftype<t> () extension met Hod
1://returns only a sequence of the shapes that is rectangles
2:var onlyrectangles = Shapes. Oftype<rectangle> (). ToList ();
Now we had a sequence of only the rectangles in the original sequence, we can also use this to chain other Queri ES, depend on rectangles, such as:
2://5 units wide ...
3:var onlybigrectangles = Shapes. Oftype<rectangle> ()
4: . Where (r = r.width > 5)
5: . ToList ();
The oftype<rectangle> () would filter the sequence to only the items that is of type Rectangle (or A subclass of it), and that results in an ienumerable<rectangle>, we can then apply the other LINQ Extensio n methods to query that list further.
Just as cast<tresult> () is a extension method on IEnumerable (and not ienumerable<t> ), the same is true for oftype<t> (). This means the can use oftype<tresult> () in object-based collections as well.
For example, given an ArrayList containing Shapes, as below:
1://object-based collections is a sequence of object
2:var shapes = new ArrayList
3: {
4: new Rectangle {Width = 3, Height = 5},
5: new Rectangle {Width = ten, Height = 13},
6: new Circle {Radius = 10},
7: new Square {Side = 13},
8: //...
9:};
We can use oftype<rectangle> to filter the sequence to only Rectangle items (and subclasses), an D then chain the other LINQ expressions, since we'll then be of type ienumerable<rectangle>:
2://containing only Rectangle or sub-types of Rectangle.
3:var onlybigrectangles = Shapes. Oftype<rectangle> ()
4: . Where (r = r.width > 5)
5: . ToList ();
Summary
So now we ' ve seen-different ways to get a sequence of a superclass or interface-a more specific sequence of a Subclass or implementation. The cast<tresult> () method casts every item in the source sequence to type TResult, and the of Type<tresult> () method selects only those items under the source sequence that is of type TResult.
You can use the these to downcast sequences, or adapt older types and sequences if only implement IEnumerable (such As DataTable, ArrayList, etc.).