Once again, in this series of posts I look at the parts of the .NET Framework that 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 be found here.

We’ve seen how the Select() extension method lets you project a sequence from one type to a new type which is handy for getting just parts of items, or building new items.  But what happens when the items in the sequence are already the type you want, but the sequence itself is typed to an interface or super-type instead of the sub-type you need?

For example, you may have a sequence of Rectangle stored in an IEnumerable<Shape> and want to consider it an IEnumerable<Rectangle> sequence instead.

Today we’ll look at two handy extension methods, Cast<TResult>() and OfType<TResult>() which help you with this task.

Cast<TResult>() – Attempt to cast all items to type TResult

So, the first thing we can do would be to attempt to create a sequence of TResult from every item in the source sequence.  Typically we’d do this if we had an IEnumerable<T>where we knew that every item was actually a TResult where TResult inherits/implements T.

For example, assume the typical Shape example classes:

  1. 1: // abstract base class
  1. 2: public abstract class Shape { }
  1. 3: 
  1. 4: // a basic rectangle
  1. 5: public class Rectangle : Shape
  1. 6: {
  1. 7: public int Widtgh { get; set; }
  1. 8: public int Height { get; set; }
  1. 9: }

And let’s assume we have a sequence of Shape where every Shape is a Rectangle

  1. 1: var shapes = new List<Shape>
  1. 2: {
  1. 3: new Rectangle { Width = 3, Height = 5 },
  1. 4: new Rectangle { Width = 10, Height = 13 },
  1. 5: // ...
  1. 6: };

To get the sequence of Shape as a sequence of Rectangle, of course, we could use a Select() clause, such as:

  1. 1: // select each Shape, cast it to Rectangle
  1. 2: var rectangles = shapes
  1. 3: .Select(s => (Rectangle)s)
  1. 4: .ToList();

But that’s a bit verbose, and fortunately there is already a facility built in and ready to use in the form of the Cast<TResult>() extension method:

  1. 1: // cast each item to Rectangle and store in a List<Rectangle>
  1. 2: var rectangles = shapes
  1. 3: .Cast<Rectangle>()
  1. 4: .ToList();

However, we should note that if anything in the list cannot be cast to a Rectangle, you will get an InvalidCastException thrown at runtime.  Thus, if our Shape sequence had a Circle in it, the call to Cast<Rectangle>() would have failed.  As such, you should only do this when you are reasonably sure of what the sequence actually contains (or are willing to handle an exception if you’re wrong).

Another handy use of Cast<TResult>() is using it to convert an IEnumerable to an IEnumerable<T>.  If you look at the signature, you’ll see that the Cast<TResult>() extension method actually extends the older, object-based IEnumerable interface instead of the newer, generic IEnumerable<T>.

This is your gateway method for being able to use LINQ on older, non-generic sequences.  For example, consider the following:

  1. 1: // the older, non-generic collections are sequence of object
  1. 2: var shapes = new ArrayList
  1. 3: {
  1. 4: new Rectangle { Width = 3, Height = 13 },
  1. 5: new Rectangle { Width = 10, Height = 20 },
  1. 6: // ...
  1. 7: };

Since this is an 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 do this:

  1. 1: // compiler error, Where() operates on IEnumerable<T>, not IEnumerable
  1. 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. 1: // ah, that’s better!
  1. 2: var bigRectangles = shapes.Cast().Where(r => r.Width > 5);

Or, if you prefer, in LINQ query expression syntax:

  1. 1: var bigRectangles = from s in shapes.Cast>()
  1. 2: where s.Width > 5
  1. 3: select s;

One quick warning: Cast<TResult>() only attempts to cast, it won’t perform a cast conversion.  That is, consider this:

  1. 1: var intList = new List<int> { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
  1. 2: 
  1. 3: // casting ints to longs, this should work, right?
  1. 4: var asLong = intList.Cast<long>().ToList();

Will 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, which means that it will 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 will succeed because there is a conversion from int to long.  But a cast from int to object to long will not, because you can only unbox an item by casting it to its exact type.

For more information on why cast-converting boxed values doesn’t work, see this post on The Dangers of Casting Boxed Values (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 items of the sequence to be of a specific type.  But what do we do when a sequence contains many different types, and we are 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 want to select all of the Rectangle instances?  Well, let’s say we had this sequence of Shape:

  1. 1: var shapes = new List<Shape>
  1. 2: {
  1. 3: new Rectangle { Width = 3, Height = 5 },
  1. 4: new Rectangle { Width = 10, Height = 13 },
  1. 5: new Circle { Radius = 10 },
  1. 6: new Square { Side = 13 },
  1. 7: // ...
  1. 8: };

Well, we could get the rectangles using Select(), like:

  1. 1: var onlyRectangles = shapes.Where(s => s is Rectangle).ToList();

But fortunately, an easier way has already been written for us in the form of the OfType<T>() extension method:

  1. 1: // returns only a sequence of the shapes that are Rectangles
  1. 2: var onlyRectangles = shapes.OfType<Rectangle>().ToList();

Now we have a sequence of only the Rectangles in the original sequence, we can also use this to chain other queries that depend on Rectangles, such as:

  1. 1: // select only Rectangles, then filter to only those more than
  1. 2: // 5 units wide...
  1. 3: var onlyBigRectangles = shapes.OfType<Rectangle>()
  1. 4: .Where(r => r.Width > 5)
  1. 5: .ToList();

The OfType<Rectangle>() will filter the sequence to only the items that are of type Rectangle (or a subclass of it), and that results in an IEnumerable<Rectangle>, we can then apply the other LINQ extension methods to query that list further.

Just as Cast<TResult>() is an extension method on IEnumerable (and not IEnumerable<T>), the same is true for OfType<T>().  This means that you can use OfType<TResult>() on object-based collections as well.

For example, given an ArrayList containing Shapes, as below:

  1. 1: // object-based collections are a sequence of object
  1. 2: var shapes = new ArrayList
  1. 3: {
  1. 4: new Rectangle { Width = 3, Height = 5 },
  1. 5: new Rectangle { Width = 10, Height = 13 },
  1. 6: new Circle { Radius = 10 },
  1. 7: new Square { Side = 13 },
  1. 8: // ...
  1. 9: };

We can use OfType<Rectangle> to filter the sequence to only Rectangle items (and subclasses), and then chain other LINQ expressions, since we will then be of type IEnumerable<Rectangle>:

  1. 1: // OfType() converts the sequence of object to a new sequence
  1. 2: // containing only Rectangle or sub-types of Rectangle.
  1. 3: var onlyBigRectangles = shapes.OfType<Rectangle>()
  1. 4: .Where(r => r.Width > 5)
  1. 5: .ToList();
Summary

So now we’ve seen two different ways to get a sequence of a superclass or interface down to 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 OfType<TResult>() method selects only those items in the source sequence that are of type TResult.

You can use these to downcast sequences, or adapt older types and sequences that only implement IEnumerable (such as DataTable, ArrayList, etc.).

C#/.NET Little Wonders: Use Cast() and OfType() to Change Sequence Type(zz)的更多相关文章

  1. EntityFramework异常The specified cast from a materialized 'System.Double' type to the 'System.Single' type is not valid.

    实体类: public class ReportEntity { public string FactorName { get; set; } public double MaxVal { get; ...

  2. entity framework异常 The specified cast from a materialized 'System.Int32' type to the 'System.String' type is not valid

    ROW_NUMBER() OVER (ORDER BY (select Null)) AS Id entity framework 查询中有这句会有异常

  3. .NET LINQ 转换数据类型

    转换数据类型      转换方法更改输入对象的类型.      LINQ 查询中的转换运算可用于各种应用程序.下面是一些示例: Enumerable.AsEnumerable<TSource&g ...

  4. 第5章 LINQ

    5.4 LINQ查询运算符 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  5. C#基础之IEnumerable

    1.IEnumerable的作用 在使用Linq查询数据时经常以IEnumerable<T>来作为数据查询返回对象,在使用foreach进行遍历时需要该对象实现IEnumerable接口, ...

  6. csharp: get Web.Services WebMethod

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  7. 从扩展方法到匿名方法再到LINQ

    1.首先我们应该知道什么是扩展方法: 扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样 ...

  8. C# LiNq的语法以及常用的扩展方法

    首先先来扯一下,这篇博文是我第一次写的,主要是我的一些摘录,希望对大家有所帮助. Linq的基础 •LINQ(读音link):Linq To SQL(过时).Linq To Object.Linq T ...

  9. IEnumerable

    C#基础之IEnumerable 1.IEnumerable的作用 在使用Linq查询数据时经常以IEnumerable<T>来作为数据查询返回对象,在使用foreach进行遍历时需要该对 ...

随机推荐

  1. error TRK0002

    运行程序出现error TRK0002的原因是因为3ds max中打开了程序生成的模型,同时使用导致memory conflict,然后随之出现一些乱七八糟的问题. 只要将3ds max重置即可,即不 ...

  2. Centos以rpm方式进行安装MySql

    安装过很多次mysql了,却没好好总结过,每次安装完了都忘,下次还要重新Google,这次总结下,自己以后也有的查. 1.安装采用的的rpm包的方式,安装前要先看系统内是否安装了旧版本的MySql和m ...

  3. spring集成activeMQ

    1.安装activehttp://activemq.apache.org/activemq-5140-release.html2.运行D:\apache-activemq-5.14.0\bin\win ...

  4. 魔法禁书目录2:回家(codevs 3024)

    题目描述 Description 大妈打完三战回家,我知道他是怎么回来的,欧洲到日本有L个站点他决定乘坐恰好n次飞机(不是学院都市的超音速飞机)和m次火车来从第一个站点到达最后一个站点.但是有一点很重 ...

  5. iPhone:4.7 5.5 4 3.5 对应的各个设备屏幕尺寸对应的像素及App上线信息

    Shared App Information You can access these properties from the App Details page in the App Informat ...

  6. php 复习

    <?php 一.php基础语法1.输出语句:echo print print_r var_dump() 2.php是弱类型语言强制转换类型: (类型)变量 settype(变量,类型) 3.变量 ...

  7. js获取url参数值(HTML之间传值)

    <h3>未设置设备: <a href="javascript:addTab('设备列表','PKE_DeviceContent/PKE_DeviceContent.aspx ...

  8. Delphi的文件操作

    参考自:http://www.cnblogs.com/railgunman/articles/1800318.html Delphi 中默认有input 和 output 两个文件变量,使用可以不用定 ...

  9. 第二十三篇:在SOUI中使用LUA脚本开发界面

    像写网页一样做客户端界面可能是很多客户端开发的理想. 做好一个可以实现和用户交互的动态网页应该包含两个部分:使用html做网页的布局,使用脚本如vbscript,javascript做用户交互的逻辑. ...

  10. C# jsonhelper

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Sc ...