for WPF developers
Home Profile Tips 全記事一覧

OfType 拡張メソッドで型によるフィルタリングをおこなう

(2017/03/08 18:30:39 created.)

(2017/03/13 8:39:19 modified.)

OfType 拡張メソッドはシーケンスの各要素を型でフィルタリングをおこないます。次のサンプルコードでその動作を見てみましょう。

まず準備としてカスタムクラスである Person クラスと Member クラスを定義します。Member クラスは Person クラスから派生しています。

Person.cs
  1. namespace Tips_Linq
  2. {
  3.     using System;
  4.  
  5.     /// 
  6.     /// 人物データを表します。
  7.     /// 
  8.     public class Person
  9.     {
  10.         /// 
  11.         /// 氏名を取得または設定します。
  12.         /// 
  13.         public string Name { get; set; }
  14.  
  15.         /// 
  16.         /// 年齢を取得または設定します。
  17.         /// 
  18.         public int Age { get; set; }
  19.     }
  20. }
Member.cs
  1. namespace Tips_Linq
  2. {
  3.     /// 
  4.     /// メンバーデータを表します。
  5.     /// 
  6.     public class Member : Person
  7.     {
  8.         /// 
  9.         /// メンバー ID を取得または設定します。
  10.         /// 
  11.         public int ID { get; set; }
  12.     }
  13. }

これらのクラスを使用して次のようなコードを実行してみましょう。

Program.cs
  1. namespace Tips_Linq
  2. {
  3.     using System;
  4.     using System.Collections.Generic;
  5.     using System.Linq;
  6.  
  7.     class Program
  8.     {
  9.         static void Main(string[] args)
  10.         {
  11.             var people = GetPeople();
  12.             foreach (var person in people)
  13.             {
  14.                 Console.WriteLine(person.Name);
  15.             }
  16.  
  17.             Console.WriteLine("Member クラスでフィルタリングします。");
  18.             var members = people.OfType<Member>();
  19.             foreach (var member in members)
  20.             {
  21.                 Console.WriteLine(member.Name);
  22.             }
  23.  
  24.             Console.ReadKey();
  25.         }
  26.  
  27.         /// 
  28.         /// 人物コレクションの列挙子を取得します。
  29.         /// 
  30.         /// 
  31.         static IEnumerable<Person> GetPeople()
  32.         {
  33.             yield return new Person() { Name = "田中 淳平", Age = 37 };
  34.             yield return new Member() { Name = "鈴木 ほのか", Age = 26 };
  35.             yield return new Person() { Name = "小池 哲司", Age = 22 };
  36.             yield return new Member() { Name = "恩田 進", Age = 42 };
  37.             yield return new Member() { Name = "中津山 亜希子", Age = 20 };
  38.         }
  39.     }
  40. }


people 変数には Person クラス以外に Member クラスが紛れ込んでいます。この中から Member クラスだけを抽出する場合に OfType 拡張メソッドを使います。 OfType 拡張メソッドの内部実装を見てみると次のような記述があります。

ILSpy による逆コンパイル結果
  1. foreach (object current in source)
  2. {
  3.     if (current is TResult)
  4.     {
  5.         yield return (TResult)((object)current);
  6.     }
  7. }

現在のシーケンスの要素を一度 object 型にボックス化し、指定された型にボックス化解除している様子がわかります。

Cast 拡張メソッドも同様にボックス化を利用したキャストをおこなっていましたが、OfType 拡張メソッドではボックス化する前に型が正しいかどうかを if 文で判定しているため、実行時に例外が発生することがありません。そして型が正しいもののみが return されることになるため、指定された型以外の要素が除外されるようになっています。Cast 拡張メソッドについては「Cast 拡張メソッドで型変換をおこなう」を参照してください。