一緒に勉強する-C#第2版(1)を深く理解する-1.2ソートとフィルタリング

20530 ワード

1.2並べ替えとフィルタリング
1.2.1:製品の名前順のソート
public class Product

    {



        public string Name { get; set; }

        public decimal Price { get; set; }

        public static  ArrayList  GetSampleProduct()

        {



            return new ArrayList { 
            new Product{Name="West Side Story", Price= 9.99m},
            new Product{Name="Assassins", Price=14.99m},
            new Product{Name="Frogs", Price=13.99m},
            new Product{Name="Sweeney", Price=10.99m}
           }; }
public override string ToString() { return string.Format("{0}:{1}", Name, Price); } }

このProduct Listをソートするには、まず見てみましょう.NET 1.1でどのように実現したのか.
まずソートします.NET 1では必ずArrayListを使う.Sort()ですが、私たちのProductはカスタムタイプです.私たちが定義したのはシステムが提供しているタイプではありません.例えば、Arraylistにはintタイプが全部入っています(1,2,3,5,7,8,22,13).そうすれば、sortを直接使用してソートすることができます.しかし、Productは私たち自身が定義したタイプで、私たちは自分で実現することを要求しました.
私たちはこの機能を2つ選択することができます.1つ目は:
ProductにIComparableというインタフェースを継承させることです.
   static void Main(string[] args)

        {



            Product list = Product.GetSampleProduct();



            list.Sort();



            foreach (Product n in list)

            {

                Console.WriteLine(n.ToString());

            }



          

        }

    }





    public class Product:IComparable

    {



        public string Name { get; set; }

        public decimal Price { get; set; }

        public static ArrayList GetSampleProduct()

        {



            return new ArrayList { 

            new Product{Name="West Side Story", Price= 9.99m},

            new Product{Name="Assassins", Price=14.99m},

            new Product{Name="Frogs", Price=13.99m},

            new Product{Name="Sweeney", Price=10.99m}

           };

        }

        public override string ToString()

        {

            return string.Format("{0}:{1}", Name, Price);

        }





        int IComparable.CompareTo(object obj)

        {

            return Name.CompareTo(((Product)obj).Name);

        }

    }

この方法はクラス内部で実装されるため,1つのフィールドのみをソートすることができ,これは柔軟ではない.
コードにはCompareTo(((Product)obj).Name); この文では、名前は文字列で、stringタイプもIComparableインタフェースを継承し、intタイプもこのインタフェースを継承していることを示しています.このインタフェースの意味は、現在のオブジェクトと同じタイプのオブジェクトを比較することです.
 
2つ目はIComparインタフェースを継承し、
 class Program

    {

        static void Main(string[] args)

        {

            Product list = Product.GetSampleProduct();

            list.Sort(new ProductNameComparer());

            list.Sort(new ProductPriceComparer());

            foreach (Product n in list) { Console.WriteLine(n.ToString()); } }

    }

    public class Product

    {



        public string Name { get; set; }

        public decimal Price { get; set; }

        public static ArrayList GetSampleProduct()

        {



            return new ArrayList { 

            new Product{Name="West Side Story", Price= 9.99m},

            new Product{Name="Assassins", Price=14.99m},

            new Product{Name="Frogs", Price=13.99m},

            new Product{Name="Sweeney", Price=10.99m}

           };

        }

        public override string ToString()

        {

            return string.Format("{0}:{1}", Name, Price);

        }

    }





    class ProductNameComparer:IComparer

    {



        int IComparer.Compare(object x, object y)

        {

            Product first = (Product)x;

            Product second = (Product)y;



            return first.Name.CompareTo(second.Name);

        }

    }



    class ProductPriceComparer : IComparer

    {



        int IComparer.Compare(object x, object y)

        {

            Product first = x as Product;

            Product second = y as Product;



            return first.Price.CompareTo(second.Price);

        }

    }

 
IComparというインタフェースを使用して、必要に応じて複数の比較器を書いて異なるフィールドをソートすることができます.IComparableインタフェースとは異なり、まず1つの方法しかありません.
しかし、IComparableインタフェースで定義された方法は、このようなint CompareTo(object obj)である1つのパラメータプロトタイプのみである.IComparインタフェースのint Compare(object x,object y);
これで違いがわかります.
IComparableは、比較するオブジェクトのクラスで実装され、そのオブジェクトと別のオブジェクトを比較することができます.IComparerは、任意の2つのオブジェクトを比較できる個別のクラスで実装されます.
 
上記の方法は最良ではありません.Compareメソッドの強制タイプ変換では、ArrayListに文字列が含まれている場合、変換時にエラーが発生するため、安全ではありません.また、foreachの場合、リスト内のオブジェクトを暗黙的にProductに変換し、実行時に異常を投げ出すこともあります.
C#2の汎用型はこれらの問題を解決するのに役立つ.
---次にICompaerを使用してlistをソートします.上のコードのArrayListもListに変更する必要があります.そうでない場合list.Sort(new ProductNameCompaer)では、コンパレータが汎用であるため、listも汎用である必要があります.
 class Program

    {

        static void Main(string[] args)

        {



            List<Product> list = Product.GetSampleProduct();



            list.Sort(new ProductNameCompar());



            foreach (Product n in list)

            {

                Console.WriteLine(n.ToString());

            }



          

        }

    }





    public class Product

    {



        public string Name { get; set; }

        public decimal Price { get; set; }

        public static List<Product> GetSampleProduct()

        {



            return new List<Product> { 

            new Product{Name="West Side Story", Price= 9.99m},

            new Product{Name="Assassins", Price=14.99m},

            new Product{Name="Frogs", Price=13.99m},

            new Product{Name="Sweeney", Price=10.99m}

           };

        }

        public override string ToString()

        {

            return string.Format("{0}:{1}", Name, Price);

        }



    }



    public class ProductNameCompar : IComparer<Product>

    {



        public int Compare(Product x, Product y)

        {

            return x.Name.CompareTo(y.Name);

        }

    }

 
これは確かに改善されていますが、ソートが面倒だと思います.実際には、インタフェースを実現してこのことをする必要はありません.次に、リストをソートするために依頼を使用します.c#2のやり方を使用します.
class Program

    {

        static void Main(string[] args)

        {



            List<Product> list = Product.GetSampleProduct();

            list.Sort(delegate(Product x, Product y)

            {

                return x.Price.CompareTo(y.Price);

            });



            foreach (Product n in list)

            {

                Console.WriteLine(n.ToString());

            }

        }

    }





    public class Product

    {

        public string Name { get; set; }

        public decimal Price { get; set; }

        public static List<Product> GetSampleProduct()

        {



            return new List<Product> { 

            new Product{Name="West Side Story", Price= 9.99m},

            new Product{Name="Assassins", Price=14.99m},

            new Product{Name="Frogs", Price=13.99m},

            new Product{Name="Sweeney", Price=10.99m}

           };

        }

        public override string ToString()

        {

            return string.Format("{0}:{1}", Name, Price);

        }

    }

すなわち,比較を実行するためにSortメソッドに提供する依頼を定義した.
--でもc#3になると簡単になり、わかりやすくなりました.Lambda式を使用してソートします.Productクラスは変わりません.mainのコードを変更すると、次のようになります.
        static void Main(string[] args)

        {



            List<Product> list = Product.GetSampleProduct();

            list.Sort((x, y) => {

                return x.Price.CompareTo(y.Price);

            });



            foreach (Product p in list)

            {

                Console.WriteLine(p.ToString());

            }

        }

ここでdelegateキーワードを使用して依頼を導入する必要はありません.パラメータタイプを指定する必要はありません.これがlambdaの強みです.
しかし、c#3では、より便利な拡張方法でソートすることができ、降順か昇順かを簡単に制御することができます.
        static void Main(string[] args)

        {



            List<Product> list = Product.GetSampleProduct();



            foreach (Product p in list.OrderBy(x => x.Price))

            {

                Console.WriteLine(p.ToString());

            }

        }

このように書くともっと簡潔になり、読みやすさがよくなります.
これはc#2とc#3の強力な機能であり、トレンドも明らかであり、より明確で、より簡単なコードに向かって進歩している.
まとめ:
c#1:弱いタイプの比較器で、委任ソートはサポートされていません.
C#2:強いタイプのコンパレータ、依頼方法、匿名方法.
c#3:Lambda式、拡張方法、リストがソートされていない状態を維持できるようにします.