いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

Linq入門記-19 (LINQ To Object, 変換演算子, AsEnumerable)


今回で、変換演算子は終わりです。
最後の変換演算子は、「AsEnumerable」メソッドです。


AsEnumerableメソッドは、以下の書式となっています。

public static IEnumerable<TSource> AsEnumerable<TSource>(
    this IEnumerable<TSource> source
)


書式を見たら分かると思いますが、このメソッドはTSource型のシーケンスからTSource型の
シーケンスを返してくれます。


つまり、

IEnumerable<string> strings  = new List<string>();
IEnumerable<string> strings2 = strings.AsEnumerable();

という動作をしてくれます。


なんのためにあるねんこれは!って感じなんですが、前回記述しました
拡張メソッド解決を行っている場合に役に立ちます。


拡張メソッド解決を行っている場合、元のクエリ演算子ではなく
自前で定義した拡張メソッド側が呼ばれます。


本来、これでいいのですが、時と場合によっては元の拡張メソッド
(つまり、IEnumerable.Whereなど)を呼びたい時があります。


そのような場合に、AsEnumerableを利用して、強制的に
IEnumerableに変換してから、処理を行ったりします。


尚、当然ながらAsEnumerableメソッドで取得した変換後シーケンスは
元データのスナップショットではありません。なので、実行する度に評価されます。
(OfTypeメソッドやCastメソッドと同じ)


以下、サンプルです。

    #region LinqSamples-18 AND LinqSamples-19 AND 拡張メソッド解決
    public class LinqSamples18 : IExecutable
    {
        public void Execute()
        {
            Persons persons = new Persons
            {
                 new Person { Id = 1, Name = "gsf_zero1" }
                ,new Person { Id = 2, Name = "gsf_zero2" }
                ,new Person { Id = 3, Name = "gsf_zero3" }
            };

            //
            // PersonExtensionが定義されているので
            // そのまま実行すると、Whereの部分にてPersonExtension.Whereが
            // 呼ばれる.
            //
            Console.WriteLine("===== 拡張メソッドを定義した状態でそのままクエリ実行 =====");
            var query = from   aPerson in persons
                        where  aPerson.Id == 2
                        select aPerson;
            
            foreach (var aPerson in query)
            {
                Console.WriteLine(aPerson);
            }
            
            //
            // AsEnumerableメソッドを利用して、PersonsをIEnumerable<Person>に
            // 変換すると、カスタムWhere拡張メソッドは呼ばれない。
            //
            Console.WriteLine("===== AsEnumerableメソッドを利用してから、クエリ実行 =====");
            var query2 = from   aPerson in persons.AsEnumerable()
                         where  aPerson.Id == 2
                         select aPerson;
            
            foreach (var aPerson in query2)
            {
                Console.WriteLine(aPerson);
            }
        }
    }
    
    public class Person
    {
        public int    Id   { get; set; }
        public string Name { get; set; }
    }
    
    public class Persons : List<Person>
    {
    }
    
    public static class PersonExtension
    {
        public static Persons Where(this Persons self, Func<Person, bool> predicate)
        {
            var result = new Persons();
            
            Console.WriteLine("========= WHERE ========");
            foreach (var aPerson in self)
            {
                if (predicate(aPerson))
                {
                    result.Add(aPerson);
                }
            }
            
            return result;
        }
    }
    #endregion


結果は以下のようになります。

  ===== 拡張メソッドを定義した状態でそのままクエリ実行 =====
  ========= WHERE ========
  Gsf.Samples.Person
  ===== AsEnumerableメソッドを利用してから、クエリ実行 =====
  Gsf.Samples.Person