• 对像集合使用过程中的一些总结


    1. 设计可比较大小的对像   

    2. 针对数组元素的查询与转换   

    3. 针对对像集合的标准查询   

    1.设计可比较大小的对像

    众所周知,数值型变量之间可以直接比较大小

     如:

     Int i=100;

     Int j=100;

     Console.Writeline(i>j); //输出false

     之所以可以比较两个整型变量的大小,是因为在数学上两个整数之间谁大谁小有明确定义,比较两个整形变量时,实际上比较的是这两个整形变量所保存数值的大小。

     然而同将同样的方法应用于引用类型的变量 ,问题来了,代码无法通过编译

     Object o1=new object();

     Object o2=new object();

     Console.WriteLine(o1 > o2);

     上述代码之所以不能通过编译,是因为两个变量引用了两个object类形的对像,而默认情况下两个object类型对像谁大谁小是未定义的。

    但在某此具体的场景下,确定两个对像的大小是有意义的

    比如判断两个圆的大小,半径相等的圆就可以认为是相等的圆,半径越大的圆也就越大,

    如果用Circle类来表示一个圆,上述问题就转换为Circle对像的大小比较问题。

    默认情况下,.Net Formework显示什么

    Circle obj1 = new Circle() { Radio = 100 };

    Circle obj2 = new Circle() { Radio = 100 };

    Console.WriteLine(obj1 == obj2); //false

    Console.WriteLine(obj1.Equals(obj2)); //false

    虽然两个圆的半径是相等的,希望两个对像比较的结果是true,但由于对像变量obj1与obj2引用的是两个不同的对像,所以==返回false,equals也是比较两个对像是否引用同一对像,所以也返回false。

    修改代码,实现IComparable接口,自定义一个CompareTo方法,用于比较大小

        class Circle:IComparable

        {

            public double Radio = 0; //半径

            public CircleCenter center; //圆心

            public int CompareTo(object obj)

            {

                double ret = Math.Abs((obj as Circle).Radio - this.Radio);

                if (ret==0)

                    return 0;//相等

                if ((obj as Circle).Radio < this.Radio)

                {

                    return 1;//大

                }

                return -1; //小

            }

        }

    有许多情况,可能只需要知道两个对像是否相等,而不需要知道这两个对像谁大谁小,这时可以重写object类的equals方法

        public override bool Equals(object obj)

            {

                if (this.CompareTo(obj) == 0)

                {

                    return true;

                }

                return false;

            }

    重写或重载object.equals需满足三点

    1.自反性:即自身与自身相等。a.Equals(a) 返回true

    2.对称性:若a与b相等,则b与a也相等,这就是说a.Equals(b)与b.Equals(a)的值一次是相等的

    3.传递性:若a与b相等,b与c相等,则a与c相等。这就是说如果a.Equals(b)和b.Equals(c)都为true,则a.Equals(c)的值也为true

    重写了equals方法,马上就可以重载==运算符,若一个对像重载了==运算符,也必须重载!=运算符,这是一个编程的基本原则,

    完整代码见下方 

     class Program
        {
            static void Main(string[] args)
            {
                Circle obj1 = new Circle() { Radio = 100 };
                Circle obj2 = new Circle() { Radio = 100 };
                Console.WriteLine(obj1.Equals(obj2)); //true
                Console.WriteLine(obj1.CompareTo(obj2));//0
                Console.WriteLine(obj1 != obj2); //false

                Circle[] circles = new Circle[4]{new Circle{Radio=10},
                                                  new Circle{Radio=20},
                                                  new Circle{Radio=15},
                                                  new Circle{Radio=18}};
                Array.Sort(circles);
                foreach (Circle c in circles)
                {
                    Console.WriteLine(c.Radio);
                }
                    Console.ReadKey();
            }

        }
        class Circle:IComparable
        {
            public double Radio = 0; //半径
           // public CircleCenter center; //圆心
            public int CompareTo(object obj)
            {
                double ret = Math.Abs((obj as Circle).Radio - this.Radio);
                if (ret==0)
                    return 0;//相等
                if ((obj as Circle).Radio < this.Radio)
                {
                    return 1;//大
                }
                return -1; //小
            }
            //重写Equals方法
            public override bool Equals(object obj)
            {
                if (this.CompareTo(obj) == 0)
                {
                    return true;
                }
                return false;
            }
            //覆盖Object类的GetHashCode方法
            public override int GetHashCode()
            {
                //整数部分与小数点后3位相同的对象生成相同的哈希值
                return (int)(Radio * 1000);
            }
            //重载相关运算符
            public static bool operator ==(Circle obj1, Circle obj2)
            {
                return obj1.Equals(obj2);
            }
            public static bool operator !=(Circle obj1, Circle obj2)
            {
                return !obj1.Equals(obj2);
            }
            public static bool operator >(Circle obj1, Circle obj2)
            {
                return obj1.CompareTo(obj2)==1;
            }
            public static bool operator <(Circle obj1, Circle obj2)
            {
                return obj1.CompareTo(obj2) == -1;
            }
        }

    2.针对数组元素的查询与转换

    在某些情况下可能会需要将数组中的元素转换为另一个类型,这个工作可以通过调用数组基类Array的ConvertAll方法实现:

        class Program
        {
            static void Main(string[] args)
            {
                int[] intArr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                double[] doublearr = Array.ConvertAll<int, double>(intArr, IntToDouble);
                foreach (double d in doublearr)
                {
                    Console.WriteLine(d);
                }
                Console.ReadKey();
            }
            static Converter<int, double> IntToDouble = delegate(int value)
            {
                return (double)value;
            };

        }

    当需要循环迭代数组中的所有元素,并且对这些元素都进行同样的处理工作时,可以调用数组基类的Array的Foreach<T>方法达到目的

        class Program
        {
            static void Main(string[] args)
            {
                //源数组
                int[] SourceArr = new int[] { 0, 1, 2, 3, 4, 5, 6 };
                //目标数组
                int[] TargetArr = new int[SourceArr.Length];
                int index=0;
                Action<int> DoubleArr = delegate(int element)
                {
                    TargetArr[index++] = element * 2;
                };
                //循环源数组,把数据存入目标数组
                Array.ForEach<int>(SourceArr, DoubleArr);
                Array.ForEach<int>(TargetArr,element => { Console.WriteLine(element); });
                Console.ReadKey();
            }
        }

    实际开发中数据排序很常见,数组基类Array直接就内置了相应的排序功能,比如选择择排序,冒泡排序

    Sort方法是最简单的:例

     class Program
        {
            static void Main(string[] args)
            {
                int[] IntArr = new int[10];
                Random ran = new Random();
                for (int i = 0; i < IntArr.Length; i++)
                {
                    IntArr[i] = ran.Next(1, 100);
                }
                Array.Sort(IntArr);
                Console.ReadKey();
            }
        }

    当调用此方法时,要求数组中的元素要实现System.IComparable接口,否则此方法会抛出InvalidOperationException异常,如果数组中元素没有实现Icomparable接口 ,可以提供一个对像比较器完成排序,或者更简单些,不需要定义一个对像比较器,直接给Sort提供一个可以比较两对像大小的方法

        class Program
        {
            static void Main(string[] args)
            {
                 Random ran = new Random();
                 MyClass[] obj = new MyClass[10];
                for (int i = 0; i < obj.Length; i++)
                {
                    obj[i] = new MyClass { value = ran.Next(1, 100) };
                }

               //定义一个委托,用于比较两个对像大小,当x>y时,返回一个正数,当x=y时,返回0,当x<y时返回负数
                Comparison<MyClass> WhoIsGreater = delegate(MyClass x, MyClass y)
                {
                    if (x.value > y.value)
                        return 1;
                    else if (x.value == y.value)
                        return 0;
                    else
                        return -1;
                };

                //未排序前输出
                Array.ForEach(obj, element => { Console.Write(element.value + ","); });
                //排序

                Array.Sort(obj,WhoIsGreater);
                Console.WriteLine("");

               //排序后输出
                Array.ForEach(obj, element => { Console.Write(element.value + ","); });
                Console.ReadKey();
            }
        }

    Sort方法提供了多个形式的重载,有一个重载可以实现两个数组的联动,假设有两个一样长度的数组(分别叫keys和Itmes),这两个数组的元素是一一对应的,这就是说,keys数组的第一个元素对应Items数据中的第一个元素,对Keys数组中的元素进行排序同时也会更改Items中的元素位置,以维护这两个数据元素间的连动关系。示例中字符串数组存放代表一周的七个英文单词,而int数组则保存1-7个整数,并且与字符串数组的星期单词是连动的

        class Program
        {
            static void Main(string[] args)
            {
                int[] Keys = new int[] { 7, 2, 3, 4, 1, 5, 6 };
                string[] Items = new string[] { "Sunday", "Wednesssday", "Thursday", "Tuesday", "Monday", "Friday", "Saturday" };  

                //对keys排序,对应的items也会跟着变

                Array.Sort<int,string>(Keys,Items);
                Array.ForEach(Keys, element => { Console.Write(element + ","); });
                Console.WriteLine("");
                Array.ForEach(Items, element => { Console.Write(element + ","); });
                Console.ReadKey();
            }
        }

    有时可能希望保存在数组中的元素都满足特定的要求,数组基类Array提供了相应的静态方法完成这一工作

    TrueForAll<T>方法所有的条件是否都满足,只要有一个不满足就返回false,代码示例如下:

        class Program
        {
            static void Main(string[] args)
            {
                Predicate<int> match = delegate(int value)
                {
                    return value % 2 == 0;
                };
                int[] IntArr=new int[]{0,1,2,3,4,5,6,7,8,9};

                //判断数组元素是否都为偶数,true全部是偶数,如果有一个不是偶数即返回false
                bool IsAllEven = Array.TrueForAll(IntArr, match);

          }
        }

    判断数组中是否存在满足某个特定元素可以用Exists<T>,代码示例如下:

        class Program
        {
            static void Main(string[] args)
            {
                int[] IntArr=new int[]{0,1,2,3,4,5,6,7,8,9};
                Predicate<int> ExistEventNumber = delegate(int value)
                {
                    if (value > 8)
                        return true;
                    return false;
                };
                bool IsExists = Array.Exists(IntArr, ExistEventNumber);
            }
        }

    TrueForAll<T>跟Exists<T>区别在于:前者全部满足指定条件才返回true,后者只要有一个满足即返回true

    数组基类Array还提供了一堆的方法用于查找元素

    Find<T>:从数组开头查找匹配元素,FindLast<T>从数组结尾开始向前查找匹配元素

    FindAll<T>:查找数据中满足特定条件的所有元素

    FindIndex<T>:查找数组中满足特定条件的元素所在的位置(索引)

    3. 对像集合的标准查询

    .Net基类库中大多数集合都实现了IEnumerable<T>接口,从.Net3.5开始,.Net基类库中提供了一组针对IEnumerable<T>接口的扩展方法,为各种对像集合提供各种标准查询服务,这些扩展方法返回一个对像集合,可以使用foreach语句逐个访问这些对像,或者将其用作数据绑定控件的数据源,这组扩展方法功能强大,使用灵活,实际构成了LINQ技术大厦的地基

    筛选:是指从对像集合中选出那些满足特定条件的对像,可通过where扩展方法实现,下图程序是从指定路径中获取包含指定字符的文件名

    完整代码如下:

    class FileBroseHelper

        {

            /// <summary>

            /// 取指定文件夹中的文件

            /// </summary>

            /// <param name="foldername"></param>

            /// <returns></returns>

            public static List<FileInfo> GetAllFileInFolder(string foldername)

            {

                try

                {

                    List<FileInfo> files = null;

                    if (!Directory.Exists(foldername))

                    {

                        return null;

                    }

                    DirectoryInfo d = new DirectoryInfo(foldername);

                    files = d.GetFiles().ToList();

                    return files;

                }

                catch (Exception ex)

                {

                    throw (ex);

                }

            }

            /// <summary>
            /// 将文件显示到listbox中
            /// </summary>
            /// <param name="file"></param>
            /// <param name="list"></param>

            public static void ShowFileListInListBox(IEnumerable<FileInfo> file, System.Windows.Forms.ListBox list)

            {

                list.Items.Clear();

                foreach (FileInfo f in file)

                {

                    list.Items.Add(f);

                }

            }

        }

    按钮事件如下:

      private void btnClick_Click(object sender, EventArgs e)
            {
                if (txtFolderNam.Text.Trim().Length == 0)
                {
                    return;
                }
                if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                {
                    //获取选择的路径
                    string foldername = folderBrowserDialog1.SelectedPath;
                    labTitle.Text = foldername;
                    //取该路径下所有文件
                    List<FileInfo> files = FileBroseHelper.GetAllFileInFolder(foldername);
                    IEnumerable<FileInfo> ret = files.Where<FileInfo>(file =>file.Name.IndexOf(txtFolderNam.Text)!=-1);
                    //把筛选出来的文件名显示在listbox中
                    FileBroseHelper.ShowFileListInListBox(ret, listBox1);
                }
            }

    上述代码中Lambda表达式给Where扩展方法赋值,用到是的FilInfo是System.IO命名空间中的类,封装了文件的基本信息和常用操作。

    投影:指把某对像集合中对像的部分属性抽取出来

    数据转换:指将某对像集合中感兴趣的对像(或抽取它的部分字段/属性)转换为另一种类型的对像

    代码见下方:

        class Program
        {
            static void Main(string[] args)
            {
                //转换为不同类型的集合
                IEnumerable<string> filelist = Directory.GetFiles("c:\\", "*.*");
                IEnumerable<FileInfo> files = filelist.Select(file => new FileInfo(file));
                foreach (FileInfo f in files)
                {
                    Console.WriteLine(f);
                }
                //返回一个匿名对像的集合
              var items =filelist.Select(file=>{
                                                FileInfo f=new FileInfo(file);
                                                return new { FileName = f.Name, Size = f.Length };
                                               });
              foreach (var i in items)
              {
                  Console.WriteLine(i.FileName + "," + i.Size);
              }
                Console.ReadKey();
            }
        }

    排序:使用扩展方法OrderBy(升序),OrderByDescending(降序)进行排序

    代码见下方:

        class Program
        {
            static void Main(string[] args)
            {
                Pet[] pets = new Pet[]{new Pet{Name="Tiger",Age=22},
                new Pet{Name="Lion",Age=17},
                new Pet{Name="rabbit",Age=18}};
                IEnumerable<Pet> query = pets.OrderBy(pet => pet.Age);
                foreach (var q in query)
                {
                    Console.WriteLine(q.Age);
                }
                Console.ReadKey();

            }
        }
        class Pet
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }

    数据连接:指根据某个对像的属性将两个数据集合的对像连接起来,得到一个新的集合

        class Program
        {
            static void Main(string[] args)
            {
                A[] a = new A[10];
                B[] b = new B[10];
                for (int i = 0; i <10; i++)
                {
                    a[i] = new A { AID = i, AName = "AName" + i.ToString() };
                    b[i] = new B { BID = i, BName = "BName" + i.ToString() };
                }
                var result = a.Join(b, aobj => aobj.AID, bobj => bobj.BID, (aobj, bobj) => new { aobj.AID, aobj.AName, bobj.BName });
                foreach (var elem in result)
                {
                    Console.WriteLine("{0},{1},{2}", elem.AID, elem.AName, elem.BName);
                }
                Console.ReadKey();
            }
        }
        class A
        {
            public int AID;
            public string AName;
        }
        class B
        {
            public int BID;
            public string BName;
        }

    集合运算:常用的集合运算包括求交集,并集,差集等,.Net基类库提供了一组扩展方法来完成标准集合运算,例如union,contact,sum,intersect等

    示例代码如下:

     class Program
        {
            static void Main(string[] args)
            {
                //创建一个多态对象集合
                IEnumerable<object> stuff = new object[] { new object(), 1, 3, 5, 7, 9, "\"thing\"", Guid.NewGuid() };
                //输出数组的内容
                Print("Stuff集合的内容: {0}", stuff);

                //偶数集合
                IEnumerable<int> even = new int[] { 0, 2, 4, 6, 8 };
                Print("偶数集合的内容: {0}", even);

                //从多态集合stuff中筛选中整数元素(全部为奇数)组成一个新集合
                IEnumerable<int> odd = stuff.OfType<int>();

                Print("奇数集合的内容: {0}", odd);

                //求两个集合的并集
                IEnumerable<int> numbers = even.Union(odd);
                Print("奇数与偶数集合的并集,成为一个整数集合: {0}", numbers);

                Print("整数集合与偶数集合的并集: {0}", numbers.Union(even));

                Print("整数集合与奇数集合相连接: {0}", numbers.Concat(odd));

                Print("整数集合与偶数集合的交集: {0}", numbers.Intersect(even));

                Print("整数集合与奇数集合连接,再删除重复值: {0}", numbers.Concat(odd).Distinct());


                if (!numbers.SequenceEqual(numbers.Concat(odd).Distinct()))
                {
                    throw new Exception("Unexpectedly unequal");
                }
                else
                {

                    Print("反转整数集合: {0}", numbers.Reverse());

                    Print("求整数集合的平均值: {0}", numbers.Average());
                    Print("求整数集合的总和: {0}", numbers.Sum());
                    Print("求整数集合的最大值: {0}", numbers.Max());
                    Print("求整数集合的最小值: {0}", numbers.Min());
                }

                Console.ReadKey();
            }

            private static void Print<T>(string format, IEnumerable<T> items)
            {
                StringBuilder text = new StringBuilder();

                foreach (T item in items.Take(items.Count() - 1))
                {
                    text.Append(item + ", ");
                }


                text.Append(items.Last());
                Console.WriteLine(format, text);
            }

            private static void Print<T>(string format, T item)
            {
                Console.WriteLine(format, item);
            }
        }

  • 相关阅读:
    python全栈开发day76-博客主页
    python全栈开发day75-用户注册页面ajax实现,用户头像上传、预览、展示
    python全栈开发day74-普通验证码和滑块验证码
    python全栈开发day73-Django认证系统
    python全栈开发day72-django之Form组件
    python全栈开发day70-Django中间件
    python全栈开发day69-cookie、session
    python全栈开发day68-ORM操作:一般操作、ForeignKey操作、ManyToManyField、聚合查询和分组查询、F查询和Q查询等
    选择排序
    冒泡排序
  • 原文地址:https://www.cnblogs.com/75115926/p/3062290.html
Copyright © 2020-2023  润新知