C#LINQ需要の進化

20374 ワード

LinqはC#3.0に導入され,C#2.0では集合から条件を満たすレコードをフィルタリングする実装方式が実現される.
BookクラスとBookクラスの集合があると仮定すると,集合から単価が50より大きいBookを調べる必要がある.
1、固定クエリーフィールドの実現方法:
  Book.csクラス:
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    public class Book
    {
        public string Title { get; set; }

        public decimal Price { get; set; }

        public string Author { get; set; }

        public string ISBN { get; set; }
    }
}

  Help.csクラス:
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    public class Helper
    {
        public static IList<Book> SearchBookByPrice()
        {
            IList<Book> books = new List<Book> {
                new Book{Title="Book1", Author="Author1", ISBN="ISBN1", Price=30},
                new Book{Title="Book2", Author="Author2", ISBN="ISBN2", Price=40},
                new Book{Title="Book3", Author="Author3", ISBN="ISBN3", Price=50},
                new Book{Title="Book4", Author="Author4", ISBN="ISBN4", Price=60}
            };

            IList<Book> results = new List<Book>();
            foreach (Book book in books)
            {
                if (book.Price >= 50)
                {
                    results.Add(book);
                }
            }

            return results;
        }
    }
}

  Program.csクラス:
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (Book book in Helper.SearchBookByPrice())
            {
                Console.WriteLine("{0}-{1}", book.Title, book.Price);
            }
        }
    }
}

上記のコードでは,Priceクエリセットによる50より大きいレコードが実現されている.しかし、需要が変動し、Titleに従ってクエリーする必要がある場合、上記の実装方法は、Titleによるクエリーを記述する方法を別途行う必要がある.
2、不定クエリーフィールドの実現方式
クエリー条件が最後に返されるのはtrueまたはfalseだけです.新しい定義の方法では、if文にtrueとしてのレコードが返され、セットに追加される限り、クエリー条件が具体的に何であるかを知る必要はありません.
  Helper.csクラス:
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    public class Helper
    {
        public delegate bool Condition(Book book);

        public bool ConditionTitle(Book book)
        {
            return book.Title == "Book2";
        }

        public bool ConditionPrice(Book book)
        {
            return book.Price >= 50;
        }

        public static IList<Book> SearchBookByCondition(Condition condition)
        {
            IList<Book> books = new List<Book> {
                new Book { Title = "Book1", Author = "Author1", ISBN = "ISBN1", Price = 30 },
                new Book { Title = "Book2", Author = "Author2", ISBN = "ISBN2", Price = 40 },
                new Book { Title = "Book3", Author = "Author3", ISBN = "ISBN3", Price = 50 },
                new Book { Title = "Book4", Author = "Author4", ISBN = "ISBN4", Price = 60 }
            };
            

            IList<Book> results = new List<Book>();

            foreach (Book book in books)
            {
                if (condition(book))
                {
                    results.Add(book);
                }
            }

            return results;
        }
    }
}

  Program.csクラス:
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //Helper.Condition condition = new Helper.Condition(new Helper().ConditionTitle);
            Helper.Condition condition = new Helper().ConditionTitle;
            foreach (Book book in Helper.SearchBookByCondition(condition))
            {
                Console.WriteLine("{0}-{1}", book.Title, book.Price);
            }
        }
    }
}

以上の実装方式は依頼delegateを採用し,C#2.0では匿名の方法も提供され,集合中のクエリ条件をフィルタするコードは以下のように変更できる.
Helper.Condition condition = delegate(Book book) { return book.Title == "Book2"; };
IList<Book> results = Helper.SearchBookByCondition(delegate(Book book) { return book.Title == "Book2"; });

C#3.0でLambda式が提供されると、集合フィルタリングは以下のように実現される.
IList<Book> results = Helper.SearchBookByCondition(book => book.Title == "Book2");

しかし、このように実現するには毎回クラス名Helperが必要であり、IList自身がこの方法を持っていることを望んでおり、C#3.0は拡張方法を提供している.
3、C#3.0拡張方法の実現方式
public static class Helper
{
    public delegate bool Condtion(Book book);
 
    public static IList<Book> Where(this IList<Book> books, Condtion condition)
    {
        IList < Book > results = new List<Book>();
        foreach (Book book in books)
        {
            if (condition(book))
            {
                results.Add(book);
            }
        }
        return results;
    }
}
IList<Book> results = books.Where(book => book.Title == "Book2");

5、IEnumberable実現方式
IListはIEnumberableから継承されているため、IEnumberableを拡張することによって実現することができる.
public static class Helper
{
    public delegate bool Condtion<T>(T t);
    public static IEnumerable<T> Where<T>(this IEnumerable<T> items, Condtion<T> condition)
    {
        foreach (T t in items)
        {
            if (condition(t))
            {
                yield return t;
            }
        }
    }
}

6、汎用拡張クラス
using System;
using System.Collections.Generic;
using System.Text;

namespace LINQTest
{
    public class Extension
    {
        public delegate TResult Func<T, TResult>(T t);

        public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> filter)
        {
            foreach (T item in source)
            {
                if (filter(item))
                {
                    yield return item;
                }
            }
        }

        public static IEnumerable<TResult> Select<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector)
        {
            foreach (T item in source)
            {
                yield return selector(item);
            }
        }
    }
}
var result = books.Where(book => book.Title == "Book2").Select(book => new { 
                Key = book.Title,Value=book.Price
            });