zoukankan      html  css  js  c++  java
  • C#之集合

    C#数组,索引器与集合

    数组

    数组是具有索引的同类型对象的集合。

    声明数组

    类型[] 数组名;
    

    []会告诉编译器,这是声明了一个数组,类型指定了数组包含的元素的类型。

    可以用new关键字来实例化数组。

    默认值

    值类型数组,每个元素默认是所存类型的默认值;引用类型不能初始化为默认值,而是初始化为null

        class Person
        {
    
        }
        class Program
        {
            public static void Main()
            {
                int[] myIntArray = new int[3];
                Person[] myPersons = new Person[3];
                DisplayVals(myIntArray);
                Console.WriteLine(myPersons[0] == null);
            }
            public static void DisplayVals(params int[] intValues)
            {
                foreach (var i in intValues)
                {
                    Console.WriteLine("Display values:{0}", i);
                }
            }
        }
    

    image-20210925230741712

    params关键字

    以下代码中,DisplayVals只接受整数数组:

        class Program
        {
            public static void Main()
            {
             
                int[] explicitArray = new int[3] { 6, 7, 8 };
                DisplayVals(explicitArray);
            }
            public static void DisplayVals(int[] intValues)
            {
                foreach (var i in intValues)
                {
                    Console.WriteLine("Display values:{0}", i);
                }
            }
        }
    

    image-20210925230014707

    而使用params关键字,就不必显式地创建数组再传入可变数用的参数:

        class Program
        {
            public static void Main()
            {
                DisplayVals(3, 4, 5, 6);
                int[] explicitArray = new int[3] { 6, 7, 8 };
                DisplayVals(explicitArray);
            }
            public static void DisplayVals(params int[] intValues)
            {
                foreach (var i in intValues)
                {
                    Console.WriteLine("Display values:{0}", i);
                }
            }
        }
    

    image-20210925230157645

    一维数组的初始化

    image-20210925233643939

    image-20210925233727523

    多维数组

    • 规则数组

    规则数组是指两维或多维的数组,在经典的二维数组中,第一维是行数,第二维是列数。

    声明语法如下:

    类型[,] 数组名;
    
            public static void Main()
            {
                const int rows = 4;
                const int columns = 3;
    
                int[,] regularArray = new int[rows, columns];
    
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < columns; j++)
                    {
                        regularArray[i, j] = i + j;
                    }
                }
    
                for (int i = 0; i < rows; i++)
                {
                    for (int j=0; j < columns; j++)
                    {
                        Console.WriteLine($"regularArray[{i},{j}]:{regularArray[i, j]}");
                    }
                }
            }
    

    image-20210925232258514

    int[,]表明这是二维整数数组,实例实现是new int[rows,columns]

    正如可以用一系列大括号的值来初始化一维数组,对二维数组也可以用相似的语法来初始化:

            public static void Main()
            {
    
                const int rows = 4;
                const int columns = 3;
    
                int[,] regularArray = new int[rows, columns] 
                {
                    {1,2,3 },
                    {4,5,6 },
                    {7,8,9 },
                    {10,11,12 }
                };
    
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < columns; j++)
                    {
                        Console.WriteLine($"regularArray[{i},{j}]:{regularArray[i, j]}");
                    }
                }
            }
    
            public static void Main()
            {
    
                const int rows = 4;
                const int columns = 3;
    
                int[,] regularArray = 
                {
                    {1,2,3 },
                    {4,5,6 },
                    {7,8,9 },
                    {10,11,12 }
                };
    
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < columns; j++)
                    {
                        Console.WriteLine($"regularArray[{i},{j}]:{regularArray[i, j]}");
                    }
                }
            }
    
    

    以上两种方法。

    image-20210925234123173

    • 不规则数组

    由数组组成的数组称为不规则的数组,因为每行不必和其他的大小一致,因此它的几何表示将不是矩形。

    声明语法:

    类型[][]...
    

    中括号的数量代表了维数。

    这点与规则数组不一样,同样地,访问数组元素也是[][]的形式来访问。

    image-20210925235931323

            public static void Main()        {            //声明是不规则数组,有4行,其中第二维是一个二维规则数组            int[][,] myJagArray = new int[3][,];            //因为是不规则数组,所以对于其子数组的规模而言,都是不确定的,所以必须要用new int[,]或者new int[2,3]来实例化,使用简单的初始化是非法的            myJagArray[0] = new int[,]                {                { 1,2,3},                { 2,3,4}                };            myJagArray[1] = new int[,]            {                {2,3 },                {4,5 },            };            myJagArray[2] = new int[,]            {                {1,2,3,4 }            };            for (int i = 0; i < myJagArray.GetLength(0); i++)//myJagArray的第一维的长度            {                for (int j = 0; j < myJagArray[i].GetLength(0); j++)//myJagArray[0]的第一维的长度                {                    for(int k = 0; k < myJagArray[i].GetLength(1); k++)                    {                        Console.WriteLine($"myJagArray[i][j,k] is {myJagArray[i][j, k]}");                    }                }            }        }    }
    

    image-20210926000732378

    • 初始化快捷语法
    int[] arr1=new int[3]{10,20,30};//传统的方法
    int[] arr1={10,20,30};
    
    • 隐式类型数组
    int[] intArr1=new int[]{1,2,3,4};//传统
    var intArr1=new []{1,2,3,4};//隐式类型数组,从初始化推断出是int[]
    
    • 误区
    var intArr1={1,2,3,4};//错误;推断不出来类型
    

    Array类

    用方括号声明数组是C#使用Array的表示法,在后台使用C#语法,会创建一个派生自抽象基类Array的新类,就可以使用Array类为每个C#数组定义的方法和属性了,比如使用Length或者foreach,实际上是使用了Array类的GetEnumerator()方法。

    • 创建数组
    Array intArray = Array.CreateInstance(typeof(int), 5);
                for (int i = 0; i < 5; i++)
                {
                    intArray.SetValue(i * 2, i);
                }
                for(int i = 0; i < 5; i++)
                {
                    Console.WriteLine(intArray.GetValue(i));
                }
    

    image-20211004153847032

    还可以将已创建的数组强制转换为声明为int[]的数组:

    int[] intArray2 = (int[])intArray;
    
    
    class Program
        {
            public static void CreateArrayWithBounds()
            {
                int[] lengthsArray = new int[2] { 3, 5 };
                int[] boundsArray = new int[2] { 2, 3 };
                Array multiDimensionalArray = Array.CreateInstance(
                    typeof(string),
                    lengthsArray,
                    boundsArray); //3行5列,行数从2开始,列数从3开始
                Console.WriteLine("Bounds:	 Lower	 Upper");
                for (int i = 0; i < multiDimensionalArray.Rank; i++)
                {
                    Console.WriteLine(
                        "{0}:	{1}	{2}",i, multiDimensionalArray.GetLowerBound(i), multiDimensionalArray.GetUpperBound(i));
                }
                Console.WriteLine("***********************");
                //赋值
                for (int i = multiDimensionalArray.GetLowerBound(0); i <= multiDimensionalArray.GetUpperBound(0); i++)
                {
                    for (int j = multiDimensionalArray.GetLowerBound(1); j <= multiDimensionalArray.GetUpperBound(1); j++)
                    {
                        int[] indexs = new int[2] { i, j }; //multiDimensionalArray的索引数组
                        multiDimensionalArray.SetValue(i.ToString() + j, indexs);
                    }
                }
                PrintValue(multiDimensionalArray);
            }
            public static void PrintValue(Array myArray)
            {
                var enumator = myArray.GetEnumerator();
                var cols = myArray.GetLength(myArray.Rank - 1);//获取列数
                int i = 0;
                while (enumator.MoveNext()) //不断的往下读取数字,直到最后一个,返回false,结束while语句
                {
                    if (i < cols)
                    {
                        i++; 
                    }
                    else
                    {
                        Console.WriteLine(); //超过列数,进入下一行,从头计数
                        i = 1;
                    }
                    Console.Write("{0}	", enumator.Current);
                }
    
            }
            public static void Main()
            {
                CreateArrayWithBounds();
            }
    
        }
    

    image-20210926221730298

    • 复制数组

    数组实现了ICloneable接口,这个接口定义的Clone方法会创建数组的浅表副本。

    如果数组的元素是值类型,就复制数组中元素的值,如果元素是引用类型,则不复制元素,复制元素的引用:

    int[] intArray1 = { 1, 2 };
    int[] intArray2 = (int[])intArray1.Clone();
                Console.WriteLine(object.ReferenceEquals(intArray2, intArray1));
                Console.WriteLine(object.ReferenceEquals(intArray2[0],intArray1[0]));
                test[] test1 = { new test(),new test() };
                var test2 =(test[]) test1.Clone();
                Console.WriteLine(object.ReferenceEquals(test1, test2));
                Console.WriteLine(object.ReferenceEquals(test1[0],test2[0]));
    

    image-20211004161801142

    Copy有几个重载函数:
    //从第一个元素开始复制Array中的一系列元素,将它们粘贴到另一Array中(从第一个元素开始)。长度为32位整数
    public static void Copy(Array sourceArray, Array destinationArray, int length)
    //从第一个元素开始复制Array中的一系列元素,将它们粘贴到另一Array中(从第一个元素开始)。长度为64位整数
    public static void Copy(Array sourceArray, Array destinationArray, long length)
    //从指定的源索引开始,复制Array中的一系列元素,将它们粘贴到另一Array中(从指定的目标索引开始)。长度和索引指定为32位整数
    public static void Copy(Array sourceArray, int sourceIndex,Array destinationArray, int destinationIndex,int length)
    //从指定的源索引开始,复制Array中的一系列元素,将它们粘贴到另一Array中(从指定的目标索引开始)。长度和索引指定为64位整数
    public static void Copy(Array sourceArray, long sourceIndex,Array destinationArray, long destinationIndex,long length)
    例:

    Array myIntArray=Array.CreateInstance( typeof(System.Int32), 5 );
    for ( int i = myIntArray.GetLowerBound(0); i <= myIntArray.GetUpperBound(0); i++ )
    {myIntArray.SetValue( i+1, i );}
    Array myObjArray = Array.CreateInstance( typeof(System.Object), 5 );
    for ( int i = myObjArray.GetLowerBound(0); i <= myObjArray.GetUpperBound(0); i++ )
    {myObjArray.SetValue( i+26, i );}
    // Copies the first element from the Int32 array to the Object array.
    Array.Copy( myIntArray, myIntArray.GetLowerBound(0), myObjArray, myObjArray.GetLowerBound(0), 1 );
    // Copies the last two elements from the Object array to the Int32 array.
    Array.Copy( myObjArray, myObjArray.GetUpperBound(0) - 1, myIntArray, myIntArray.GetUpperBound(0) - 1, 2 );
    

    区别:
    Clone()返回值是Object,Copy返回值为void
    Clone()是非静态方法,Copy为静态方法。
    Clone()会创建一个新数组,Copy方法必须传递阶数相同且有足够元素的已有数组。

                int[] intArray1 = { 1, 2,3,4 };
                int[] intArray2 = new int[6] { 0, 0, 0, 0, 0, 0 };
                Array.Copy(intArray1, 1, intArray2, 3, 3);
                foreach(var a in intArray2)
                {
                    Console.Write(a+",");
                }
    

    image-20211004162619289

    int[] intArray1 = { 1, 2,3,4 };
    int[] intArray2 = new int[6] { 0, 0, 0, 0, 0, 0 };
    intArray1.CopyTo(intArray2, 1);
    foreach(var a in intArray2)
    {
    Console.Write(a+",");
    }
    

    image-20211004162830287

    如果需要包含引用类型的数组的深层副本,就必须迭代数组并创建新对象

    • 搜索

    在一维数组中,Array类提供了一系列的方法来找元素。

    (1)BinarySearch:对于有序数组,迅速的搜索一个特定的元素

    (2)IndexOf/LastIndex:对于无序数组,搜索特定元素

    (3)Find/FindLast/FindIndex/FindLastIndex/FindAll/Exists/TruForAll:对于无序数组,搜索满足给定条件Predicate<T>的元素,其中Predicate<T>是一个委托,接受一个object,返回true,或false.

    public delegate bool Predicate<T>(T object)

    以上搜索方法都不会抛出异常,如果没找到,对于返回类型是int的,就返回-1。发返回泛型的方法返回的是默认类型。

    string[] names = { "Rodney", "Jack", "jill" };
    string match = Array.Find(names, n => n.Contains("a"));
    Console.WriteLine(match);
    

    string[] names = { "Rodney", "Jack", "jill" };
                string match = Array.Find(names, delegate(string name) { return name.Contains("a"); });
                Console.WriteLine(match);
    

    image-20211005213131138

    • Sort

    (1)对单个数组排序

    public static void Sort<T> (T[] array);
    public static void Sort(Array array);
    

    (2)对一对数组排序

    public static void Sort<Tkey,TValue>(Tkeyy[] keys,TValue[] items);
    public static void Sort(Array keys,Array items);
    

    例子:

    int[] num = { 2, 1, 4,3};
                string[] words = { "China", "America", "Russia", "British" };
                Array.Sort(num, words);
                foreach(var s in num)
                {
                    Console.Write("{0},", s);
                }
                Console.WriteLine();
                foreach(var s in words)
                {
                    Console.Write("{0},", s);
                }
    

    image-20211005221111023

    • Copy

    四个浅层拷贝:Clone,CopyTo,Copy,ConstrainedCopy,其中前两个是实例方法,后两个是静态方法。

    对于拷贝多维数组,需要将多维数组的索引转换为一个线性的索引,比如33的数组,其中心位置,postion[1,1]的索引就是13+1=4.

                int[,] a=
                {
                    {1,2,3 },
                    {4,5,6 },
                    {7,8,9 }
                };
                int[,] b = new int[2,2];
                Array.Copy(a,5,b,0,4);
                foreach(var s in b)
                {
                    Console.WriteLine(s);
                }
    

    image-20211005222508431

    ConstraintedCopy是一种原子操作:如果所有元素不能成功的拷贝,那么该操作就一个都不操作,回滚到之前的状态。

                int[,] a=
                {
                    {1,2,3 },
                    {4,5,6 },
                    {7,8,9 }
                };
                int[,] b = new int[2,2];
                Array.ConstrainedCopy(a, 5, b,0, 4);
                foreach(var s in b)
                {
                    Console.WriteLine(s);
                }
    

    image-20211005222825778

    • Convert And Resize

    Array.ConvertAll创建和返回了TOutput类型的数组,调用了Converter委托,来沿着元素全部复制转换,其中Converter定义如下:

    public delegate TOutput Converter<IIput,TOutput>(TInput input);
    
    float[] reals = { 1.3f, 1.5f, 1.8f };
                int[] wholes = Array.ConvertAll(reals, r => Convert.ToInt32(r));
                foreach(var s in wholes)
                {
                    Console.WriteLine(s);
                }
    

    image-20211006003125583

    float[] reals = { 1.3f, 1.5f, 1.8f,2.3f };
                Array.Resize(ref reals, 5);
                Console.WriteLine(reals.Length);
                foreach(var s in reals)
                {
                    Console.WriteLine(s);
                }
    

    image-20211006003608748

    ArraySegment<T>

    ArraySegment<T>是结构类型(struct),表示数组的一段,如果需要使用不同的方法来处理某个大型数组的不同部分,可以将整个数组传给不同的方法,这些方法只使用数组的某个部分。方法的参数除了数组外,还应包括数组内的偏移量和应使用的元素数。

        class Program
        {
            public static int SumOfSegments(ArraySegment<int>[] segments)
            {
                int sum = 0;
                foreach(var segment in segments)
                {
                    for(int i = segment.Offset; i < segment.Offset + segment.Count; i++)
                    {
                        sum += segment.Array[i];
                        segment.Array[i] -= 1;//数组段不复制数组元素,如果数组段中的元素改变了,这些变化就会反映到原数组中
    
                    }
                }
                return sum;
            }
            public static void Main()
            {
                int[] arr1 = { 1, 4, 5, 11, 13, 18 };
                int[] arr2 = { 3, 4, 5, 18, 21, 27, 33 };
                var segments = new ArraySegment<int>[2]
                {
                    new ArraySegment<int>(arr1,0,3),
                    new ArraySegment<int>(arr2,3,3)
                };
                var sum = SumOfSegments(segments);
                Console.WriteLine(sum);
                foreach(var a in arr1)
                {
                    Console.WriteLine(a);
                }
                
            }
    
        }
    

    image-20211004172853733

        class Program
        {
    
            public static void change(ArraySegment<int> b)
            {
                b.Array[b.Offset] = 100;
            }
            public static void Main()
            {
                int[] a = { 1, 2, 3,4 };
                ArraySegment<int> b = new ArraySegment<int>(a, 1, 2);
                change(b);
                Console.WriteLine(a[1]);
            }
    
        }
    

    image-20211017135928154

    上例表明,ArraySegment虽然是结构,但是传入其中的数组,被修改时,即被修改,因为数组本身就是引用类型。

    本质上与下例相似:

        public struct a
        {
            public b bb;
        }
        public class b
        {
            public int x;
        }
        
        class Program
        {
    
            public static void change(a aa)
            {
                aa.bb.x = 100;
            }
            public static void Main()
            {
                var c = new a();
                c.bb = new b();
                change(c);
                Console.WriteLine(c.bb.x);
            }
    
        }
    

    image-20211017140822237

    数组转换

    如果维数相同,且引用元素类型之间可以转换,那么可以进行数组间的转换。如果元素类型可以隐式转换,则可以进行隐式转换,否则,必须进行显式转换。

    class Program
        {
            public class Employee
            {
                private int empID;
                public Employee(int empID)
                {
                    this.empID = empID;
                }
                public override string ToString()
                {
                    return empID.ToString();
                }
            }
            public class Tester
            {
                public static void PrintArray(object[] theArray)
                {
                    Console.WriteLine("Contens of the array{0}", theArray.ToString());
                    foreach(var obj in theArray)
                    {
                        Console.WriteLine("Value:{0}", obj);
                    }
                }
            }
            public static void Main()
            {
                Employee[] myArray = new Employee[3];
                for (int i = 0; i < 3; i++)
                {
                    myArray[i] = new Employee(i + 5);
                }
                Tester.PrintArray(myArray);//将myArray隐式转换为object[]类型,因为employee可隐式转换为object
            }
    
        }
    

    image-20210926222728908

    索引器

    索引器是类的成员,它能使类看起来像某种数组,可以用来索引类中的数据。

    语法:

    类型 this[类型 参数]{get;set;}
    

    第一个类型当然是返回类型,第二个类型,参数是索引的类型和参数,它和属性非常像,都有set和get访问器,this是对索引器所指对象的引用。

        public class ListBoxText
        {
            private int ctr = 0;
            private string[] strings;
            public int GetNumEntries()
            {
                return ctr;
            }
            public ListBoxText(params string[] initialStrings)
            {
                strings = new string[256];
                foreach(var s in initialStrings)
                {
                    strings[ctr++] = s;
                }
            }
            public void Add(string theString)
            {
                if (ctr >= strings.Length)
                {
                    Console.WriteLine("已经满了...");
                }
                else
                {
                    strings[ctr++] = theString;
                }
            }
            public string this[int index]
            {
                get
                {
                    if (index<0|| index > strings.Length)
                    {
                        Console.WriteLine("索引{0}不对", index);
                    }
                    return strings[index];
                }
                set
                {
                    if (index >= ctr)
                    {
                        Console.WriteLine("只能Add增加,索引只能更改已经有的数据");
                    }
                    else
                    {
                        strings[index] = value;
                    }
                }
            }
        }
    
        class Program
        {
            public static void Main()
            {
                var lbt = new ListBoxText("Hello", "world");
                lbt.Add("Who");
                lbt.Add("is");
                lbt.Add("John Yang");
    
                for (int i = 0; i < lbt.GetNumEntries(); i++)
                {
                    Console.WriteLine($"lbt[{i}]:{lbt[i]}");
                }
            }
    
        }
    

    image-20210926225550593

    索引其他值

    C#并不要求我们必须使用整数作为集合的索引,索引值其实可以重载。

        public class ListBoxText
        {
            private int ctr = 0;
            private string[] strings;
            public int GetNumEntries()
            {
                return ctr;
            }
            public ListBoxText(params string[] initialStrings)
            {
                strings = new string[256];
                foreach(var s in initialStrings)
                {
                    strings[ctr++] = s;
                }
            }
            public void Add(string theString)
            {
                if (ctr >= strings.Length)
                {
                    Console.WriteLine("已经满了...");
                }
                else
                {
                    strings[ctr++] = theString;
                }
            }
            public string this[int index]
            {
                get
                {
                    if (index<0|| index > strings.Length)
                    {
                        Console.WriteLine("索引{0}不对", index);
                    }
                    return strings[index];
                }
                set
                {
                    if (index >= ctr)
                    {
                        Console.WriteLine("只能Add增加,索引只能更改已经有的数据");
                    }
                    else
                    {
                        strings[index] = value;
                    }
                }
            }
    
            private int findString(string searchString)
            {
                for (int i = 0; i < ctr; i++)
                {
                    if (strings[i].StartsWith(searchString))
                    {
                        return i;
                    }
                }
                return -1;
            }
            //重载索引器
            public string this[string index]
            {
                get
                {
                    if (index.Length == 0)
                    {
                        Console.WriteLine("请输入要搜索的字符串");
                        return "Null";
                    }
                    var locater = findString(index);
                    if (locater == -1)
                    {
                        return "Null";
                    }
                    return this[locater];
                }
                set
                {
                    var locater = findString(index);
                    if (locater == -1)
                    {
                        Console.WriteLine("Not find");
                    }
                    else
                    {
                        strings[locater] = value;
                    }
                }
            }
        }
    
        class Program
        {
            public static void Main()
            {
                var lbt = new ListBoxText("Hello", "world");
                lbt.Add("Who");
                lbt.Add("is");
                lbt.Add("John Yang");
                lbt[1] = "Universe";
    
                lbt["Hel"] = "GoodBye";
                lbt["yang"]="jsfdk" ;
    
                for (int i = 0; i < lbt.GetNumEntries(); i++)
                {
                    Console.WriteLine($"lbt[{i}]:{lbt[i]}");
                }
    
                
    
            }
    
        }
    

    image-20210926234111952

    集合接口

    接口 目的
    ICollection 泛型集合的基接口,the Count property
    IEnumerator/IEnumerable 用foreach语句枚举集合,enumeration only
    ICollection 所有集合都要实现,以提供CopyTo()方法,以及Count,IsSynchronized和SynRoot属性
    IComparer/IComparable 比较集合中的两个对象以对集合排序
    IList 用于数组可索引的集合,random access by index
    IDictionary<K,V> 用于基于键值对的集合,如Dictionary,random access by key
    • ICollection<T> 和ICollection
    public interface ICollection<T>:IEnumerable<T>,IEnumerable
    {
        int Count{get;}
        bool Contains(T item);//查
        void CopyTo(T[] array,int arrayIndex);
        bool IsReadOnly{get;};
        void Add(T item);//增
        bool Remove(T item);//删
        void Clear();//删
    }
    
    public interface ICollection:IEnumerable
    {
        int Count{get;}
        bool IsSynchronized{get;}
        object SynRoot{get;}
        void CopyTo(Array array,int index);
    }
    

    如果ICollection是read-only的(ReadOnlyCollection),那么Add,Remove,Clear都会抛出NotSupportedException异常.

    这些接口经常与IList或者IDictionary接口一起联合使用。

    定制集合和代理(Customizable collections and proxies)

    定义的集合虽然可以快速实例化使用,但它们不能控制当元素被添加或者移除后所触发的事情。有时候,我们可能会需要这种控制:

    • 当元素被添加/删除所触发的事件
    • 因为增加的/删除的元素,而更新属性
    • 监测增加/删除的”合法性“,如果”非法“,则抛出异常

    .NET Framework在System.Collections.ObjectModel中提供了这种目的的集合类。

    Collection<T>CollectionBase

    Collection<T>是对List<T>的可定制化的包装。

    public class Collection<T>:
    IList<T>,ICollection<T>,IEnumerable<T>,IList,ICollection,IEnumerable
    {
        protected virtual void ClearItems();//删
        protected virtual void InsertItem(int index,T item);//增
        protected virtual void RemoveItem(int index);//删
        protected virtual void SetItem(int index,T item);//改
        protected IListt<T> Items{get;}//查
    }
    

    这些虚方法提供了你可以改变或者提升列表的正常行为的方法,protected意味着它的实例不能对这些方法进行使用,只能由其派生类使用这些方法。

    这些虚方法不需要被覆盖,直到有必要去改变列表的默认行为。下面例子展示了其典型应用的构架:

        public class Animal
        {
            public string Name;
            public int Popularity;
            public Animal(string name,int popularity)
            {
                Name = name;
                Popularity = popularity;
            }
        }
        public class AnimalCollection : Collection<Animal>
        {
            //AnimalCollection is already a fully functioning list of animals
            //no extra code is required.
        }
        public class Zoo
        {
            public readonly AnimalCollection Animals = new AnimalCollection();
        }
        class Program
        {
    
            public static void Main()
            {
                Zoo zoo = new Zoo();
                zoo.Animals.Add(new Animal("Kangaroo", 10));
                zoo.Animals.Add(new Animal("Tiger", 20));
                foreach (var a in zoo.Animals) Console.WriteLine(a.Name);
            }
    
        }
    

    image-20211010233135739

    正如上例所示,AnimalCollection所起的作用与简单的List<Animal>一样,但它却为将来的功能延申提供了基础。

    public class Animal
        {
            public string Name;
            public int Popularity;
            public Zoo Zoo { get; internal set; }
            public Animal(string name,int popularity)
            {
                Name = name;
                Popularity = popularity;
                
            }
        }
        public class AnimalCollection : Collection<Animal>
        {
            Zoo zoo;
            public AnimalCollection(Zoo zoo) { this.zoo = zoo; }
            protected override void InsertItem(int index, Animal item)
            {
                base.InsertItem(index, item);
                item.Zoo = zoo;
                Console.WriteLine("Index {0} insert Item {1}", index, item.Name);
            }
            protected override void SetItem(int index, Animal item)
            {
                base.SetItem(index, item);
                item.Zoo = zoo;
                Console.WriteLine("Index {0} has been reset as {1}", index, item.Name);
            }
            protected override void RemoveItem(int index)
            {
                this[index].Zoo = null;
                Console.WriteLine("index {0} has been removed", index);
                base.RemoveItem(index);
            }
            protected override void ClearItems()
            {
                foreach (var a in this) a.Zoo = null;
                base.ClearItems();
                Console.WriteLine("All has been removed now...");
            }
        }
        public class Zoo
        {
            public readonly AnimalCollection Animals;
            public Zoo() { Animals = new AnimalCollection(this); }
        }
        class Program
        {
    
            public static void Main()
            {
                Zoo zoo = new Zoo();
                zoo.Animals.Add(new Animal("Kangaroo", 10));
                zoo.Animals.Add(new Animal("Tiger", 20));
                zoo.Animals.Insert(0, new Animal("Dophin", 23));
                zoo.Animals.Add(new Animal("Python", 25));
                zoo.Animals.RemoveAt(2);
                foreach (var a in zoo.Animals) Console.WriteLine(a.Name);
                zoo.Animals.Clear();
    
            }
    
        }
    

    image-20211011220721303

    CollectionBase

    CollectionBaseCollection<T>的非泛型版本,它也有Collection<T>的特定,但使用更加臃肿,它没有InsertItem,RemoveItem,SetItem,ClearItem方法,取而代之的是OnInsert,OnInsertComplete,OnClear,OnClearComplete

    KeyedCollection<TKey,TItem>DictionaryBase

    可以认为KeyedCollection<TKey,TItem>Collection<TItem>加上通过键快速查找。

    与字典不同的是,它并没有”键-值对“的概念,它的键是从存储元素自身里获取的:通过抽象方法GetKeyForItem实现的(注意抽象方法必须被派生类override而实现,除非派生类本身也是抽象类)

    public abstractt class KeyedCollection<TKey,TItem>:Collection<TItem>
    {
        //...
        protected abstract TKey GetKeyForItem(TItem item);//抽象方法,非抽象类派生类必须覆盖之
        protected void ChangeItemKey(TItem item,TKey newKey);
        //Fast lookup by key--this is in addition to lookup by index.
        public TItem this[TKey key]{get;}
        protected IDictionary<TKey,TItem> Dictionary{get;}
    }
    

    GetKeyForItem作为抽象方法,是非抽象派生类所必须覆写的方法,其作用就是从加入的元素里抽取出信息,作为该元素的”键“。ChangeItemKey方法在元素的键被改变时候,必须被调用,以此来更新它的内部字典。而内部字典是由Dictionary只读属性维护的,当第一个元素被添加,内部字典就被创建。

    KeyedCollection<TKey,TItem>的应用场景就是可以通过索引和”键“来对集合进行访问。

        public class Animal
        {
            string name;
            public string Name
            {
                get { return name; }
                set
                {
                    if (Zoo != null) Zoo.Animals.NotifyNameChange(this, value);
                    name = value;
                }
            }
            public int Popularity;
            public Zoo Zoo { get; internal set; }
            public Animal(string name,int popularity)
            {
                Name = name;Popularity = popularity;
            }
        }
        public class AnimalCollection :KeyedCollection<string,Animal>
        {
            Zoo zoo;
            public AnimalCollection(Zoo zoo) { this.zoo = zoo; }
            internal void NotifyNameChange(Animal a,string newName)
            {
                this.ChangeItemKey(a, newName);
                Console.WriteLine("{0} has been changed as {1}", a.Name, newName);
            }
            protected override string GetKeyForItem(Animal item)//当重写时,不能更改其访问修饰符,也就是也必须为protected
            {
                Console.WriteLine("Get {0}", item.Name);
                return item.Name;
            }
            protected override void InsertItem(int index, Animal item)
            {
                base.InsertItem(index, item);
                item.Zoo = zoo;
                Console.WriteLine("Index {0} insert Item {1}", index, item.Name);
            }
            protected override void SetItem(int index, Animal item)
            {
                base.SetItem(index, item);
                item.Zoo = zoo;
                Console.WriteLine("Index {0} has been reset as {1}", index, item.Name);
            }
            protected override void RemoveItem(int index)
            {
                this[index].Zoo = null;
                Console.WriteLine("index {0} has been removed", index);
                base.RemoveItem(index);
            }
            protected override void ClearItems()
            {
                foreach (var a in this) a.Zoo = null;
                base.ClearItems();
                Console.WriteLine("All has been removed now...");
            }
    
        }
        public class Zoo
        {
            public readonly AnimalCollection Animals;
            public Zoo() { Animals = new AnimalCollection(this); }
        }
        class Program
        {
    
            public static void Main()
            {
                Zoo zoo = new Zoo();
                zoo.Animals.Add(new Animal("Kangaroo", 10));
                zoo.Animals.Add(new Animal("Mr sea lion", 20));
                Console.WriteLine(zoo.Animals[0].Popularity);
                Console.WriteLine(zoo.Animals["Kangaroo"].Popularity);
                zoo.Animals["Mr sea lion"].Name = "Mr Roo";
                Console.WriteLine(zoo.Animals["Mr Roo"].Popularity);
    
            }
    
        }
    

    image-20211012233424279

    由上面的例子,可以看出,当添加第一个元素时候,内部字典同时也被创建,并调用GetKeyForItem方法,求得该元素在内部字典中的键,显示的Get Kangroo也佐证了这点,然后紧接着,被添加到内部列表中。再添加第二个元素,同样的,调用GetKeyForItem,求得第二个元素在内部字典中的键,紧接着被加到内部列表中。当被改变元素的”键“时候,这儿是Name属性,就调用ChangeItemKey方法来改变元素的”键“,由于ChangeItemKeyprotected,因此实例不能直接访问,需要通过派生类在类内调用,这也是NotifyNameChange出现的原因。

    • IList<T>和IList

    IList<T>是关于在位置上,用索引进行增删查改的接口,它继承了ICollection<T>,和IEnumeratble<T>

    public interface IList<T>:ICollection<T>,IEnumerable<T>,IEnumerable
    {
        T this[int index]{get;set;}//查,改
        void Insert(int index,T item);//增
        void RemoveAt(int index);//删
        int IndexOf(T item);//查
    }
    
    public interface IList:ICollection,IEnumerable
    {
        object this[int index]{get;set;}
        bool IsFixedSize{get;}
        bool IsReadOnly{get;}
        int Add(object value);
        void Clear();
        bool Contains(object value);
        int IndexOf(object value);
        void Insert(int index,object value);
        void Remove(object value);
        void RemoveAt(int index);
    }
    

    List<T>就是既有IList<T>又有ILIst的典型类。

    如果试图通过 IList的索引来访问多维数组,那么ArgumentException将会被抛出。

    可能会写出这样的代码:

    public object FirstOrNull(IList list)
    {
        if(list==null || list.Count==0) return null;
        return list[0];
    }
    

    如果传入多维数组,那么就会抛出异常。

     var a = new int[,]
                {
                    {1,2,3 },
                    {3,4,5 }
                };
                var b = new int[] { 1, 2, 3 };
                var c = FirstOrNull(b as IList);
                Console.WriteLine(c);
                var d = FirstOrNull(a as IList);
                Console.WriteLine(d);
    

    image-20211005202112129

        public class ListBoxText
        {
            private int ctr = 0;
            private string[] strings;
            public int GetNumEntries()
            {
                return ctr;
            }
            public ListBoxText(params string[] initialStrings)
            {
                strings = new string[256];
                foreach(var s in initialStrings)
                {
                    strings[ctr++] = s;
                }
            }
            public void Add(string theString)
            {
                if (ctr >= strings.Length)
                {
                    Console.WriteLine("已经满了...");
                }
                else
                {
                    strings[ctr++] = theString;
                }
            }
            public string this[int index]
            {
                get
                {
                    if (index<0|| index > strings.Length)
                    {
                        Console.WriteLine("索引{0}不对", index);
                    }
                    return strings[index];
                }
                set
                {
                    if (index >= ctr)
                    {
                        Console.WriteLine("只能Add增加,索引只能更改已经有的数据");
                    }
                    else
                    {
                        strings[index] = value;
                    }
                }
            }
    
            private int findString(string searchString)
            {
                for (int i = 0; i < ctr; i++)
                {
                    if (strings[i].StartsWith(searchString))
                    {
                        return i;
                    }
                }
                return -1;
            }
            //重载索引器
            public string this[string index]
            {
                get
                {
                    if (index.Length == 0)
                    {
                        Console.WriteLine("请输入要搜索的字符串");
                        return "Null";
                    }
                    var locater = findString(index);
                    if (locater == -1)
                    {
                        return "Null";
                    }
                    return this[locater];
                }
                set
                {
                    var locater = findString(index);
                    if (locater == -1)
                    {
                        Console.WriteLine("Not find");
                    }
                    else
                    {
                        strings[locater] = value;
                    }
                }
            }
        }
    
        class Program
        {
            public static void Main()
            {
                var lbt = new ListBoxText("Hello", "world");
                lbt.Add("Who");
                lbt.Add("is");
                lbt.Add("John Yang");
                lbt[1] = "Universe";
    
                lbt["Hel"] = "GoodBye";
                lbt["yang"]="jsfdk" ;
    
                for (int i = 0; i < lbt.GetNumEntries(); i++)
                {
                    Console.WriteLine($"lbt[{i}]:{lbt[i]}");
                }
    
            }
    
        }
    

    image-20210927215229468

    约束

    以下面的单链表为例,所谓单链表就是当增加新节点时候,链表自动实现排序;为此,需要定义一个Node类,此类包裹数据,并需要实现IComparable接口,有next,prev字段,保存了它的前一个Node,和后一个Node,然后定义一个Add方法,使得每次调用都返回起始端的Node,这就需要将本节点与Add传入的参数Node作比较,如果传入参数的Node比本节点靠前,那么就要调整相互关系,即调整next,prev字段,返回传入参数的Node,如果传入的参数的Node 比本节点靠后,返回本节点,直接用Add方法操作传入参数的Node,让其找到自己在链表中的合适的位置,即它的后一个节点一定是在它后面,也就是说不再调用Add向后传播。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Xml.Linq;
    
    namespace ConsoleApp
    {
           public class Employee : IComparable<Employee>
        {
            private string name;
            public Employee(string name)
            {
                this.name = name;
            }
            public override string ToString()
            {
                return this.name;
            }
            public int CompareTo(Employee rhs)
            {
                return this.name.CompareTo(rhs.name);
            }
            public bool Equals(Employee rhs)
            {
                return this.name == rhs.name;
            }
        }
        public class Node<T>:IComparable<Node<T>> 
            where T : IComparable<T> //约束,使得T必须实现IComparable接口,从而一定有CompareTo方法
        {
            private T data;
            private Node<T> next = null;
            private Node<T> prev = null;
            public Node(T data)
            {
                this.data = data;
            }
            public T Data { get { return data; } }
    
            public Node<T> Next { get { return this.next; } }
            public Node<T> Prev { get { return this.prev; } }
    
            public int CompareTo(Node<T> rhs)
            {
                return this.data.CompareTo(rhs.data);//data属于类型T,由约束可知,一定有CompareTo方法
            }
            
            public bool Equals(Node<T> rhs)
            {
                return this.data.Equals(rhs.data);
            }
            public Node<T> Add(Node<T> newNode)
            {
                if (this.CompareTo(newNode) > 0) //在我之前
                {
                    newNode.next = this;//安插在我前面,首先更改newNode的next,然后根据我的prev有无来做安排
                    if (this.prev != null) //我之前有结点
                    {
                        this.prev.next = newNode;
                        newNode.prev = this.prev;
                    }
                    this.prev = newNode;
                    return newNode;
                }
                else //在我之后
                {
                    if (this.next != null)
                    {
                        this.next.Add(newNode);
                    }
                    else
                    {
                        this.next = newNode;
                        newNode.prev = this;
                    }
                    return this;
                }
    
            }
            public override string ToString()
            {
                string output = data.ToString();
                if (next != null)
                {
                    output += "," + next.ToString();
                }
                return output;
            }
    
        }
        public class LinkedList<T>
            where T : IComparable<T>
        {
            private Node<T> headNode = null;
            public LinkedList() { }
            public void Add(T data)
            {
                if (headNode == null)
                {
                    headNode = new Node<T>(data);
                }
                else
                {
                    headNode = headNode.Add(new Node<T>(data));
                }
            }
            public override string ToString()
            {
                if (this.headNode != null)
                {
                    return this.headNode.ToString();
                }
                else
                {
                    return string.Empty;
                }
            }
            public T this[int index]
            {
                get
                {
                    int ctr = 0;
                    Node<T> node = headNode;
                    while(node!=null && ctr <= index)
                    {
                        if (ctr == index)
                        {
                            return node.Data;
                        }
                        else
                        {
                            node = node.Next;
                        }
                        ctr++;
                    }
                    throw new ArgumentOutOfRangeException();
                }
            }
        }
    
        class Program
        {
            public static void Main()
            {
                var myLinkedList = new LinkedList<int>();
                Random rand = new Random();
                Console.WriteLine("Adding...");
                for (int i = 0; i < 10; i++)
                {
                    int nextInt = rand.Next(10);
                    myLinkedList.Add(nextInt);
                }
                Console.WriteLine("integers:" + myLinkedList.ToString());
    
    
                var employees = new LinkedList<Employee>();
                employees.Add(new Employee("John"));
                employees.Add(new Employee("Paul"));
                employees.Add(new Employee("George"));
                employees.Add(new Employee("Ringo"));
                employees.Add(new Employee("Ahad"));
    
                Console.WriteLine("Emplyees:" + employees);
                Console.WriteLine($"employees[2]:{employees[2]}");
            }
    
        }
    }
    
    
    

    image-20210927233153308

    List<T> 和ArrayList

    与所有集合一样,List也要实现IComparable接口,从而可以调用Sort方法,其接口要求实现CompareTo(T rhs)方法。当调用Sort方法时,会调用IComparer的默认实现,它会使用QuickSort调用List中每个元素的CompareTo()的IComparable实现。

    当要控制排序顺序如何定义时,特别是多种排序定义时候,可以自行创建IComparer实现,它要求实现Compare(T rhs,T lhs)

    • 通过List中元素T实现IComparable,向Sort()传入实现IComparer的类进行排序
        public class Employee : IComparable<Employee>
        {
            private int empID;
            private int yearsOfSvc;
            public Employee(int empID)
            {
                this.empID = empID;
            }
            public Employee(int empID,int yearsOfSvc)
            {
                this.empID = empID;
                this.yearsOfSvc = yearsOfSvc;
            }
            public override string ToString()
            {
                return "ID:" + empID.ToString()+
                    ".Years of Svc" + yearsOfSvc.ToString();
            }
            public int CompareTo(Employee rhs, Employee.EmployeeComparer.ComparisonType which) //该方法是EmployeeCompareer类的Compare方法的要求
            {
                switch (which)
                {
                    case Employee.EmployeeComparer.ComparisonType.EmpID:
                        return this.empID.CompareTo(rhs.empID);
                    case Employee.EmployeeComparer.ComparisonType.Yrs:
                        return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
                }
                return 0;
            }
            public int CompareTo(Employee rhs) //该方法是IComparable的硬性要求
            {
                return this.empID.CompareTo(rhs.empID);
            }
    
            public static EmployeeComparer GetComparer()
            {
                return new EmployeeComparer();
            }
            
            
    
            public class EmployeeComparer : IComparer<Employee>
            {
                public enum ComparisonType
                {
                    EmpID,
                    Yrs
                };
                private ComparisonType whichComparison;
                public ComparisonType WhichComparison
                {
                    get { return whichComparison; }
                    set { whichComparison = value; }
                }
                public int Compare(Employee rhs,Employee lhs)
                {
                    return rhs.CompareTo(lhs, whichComparison);
                }
            }
        }
    
        class Program
        {
            public static void Main()
            {
                List<Employee> empArray = new List<Employee>();
                Random r = new Random();
                for (int i = 0; i < 5; i++)
                {
                    empArray.Add(new Employee(r.Next(10) + 100, r.Next(20)));
                }
    
                Console.WriteLine("Initial....");
                for (int i = 0; i < 5; i++)
                {
                    Console.WriteLine(empArray[i]);
                }
    
                var comparer = Employee.GetComparer();//创建EmployeeComparer实例
                comparer.WhichComparison = Employee.EmployeeComparer.ComparisonType.EmpID;//为实例的WhichComparison属性赋值
                empArray.Sort(comparer);//Sort方法调用,将实现IComparer的comparer实例传入
                Console.WriteLine("After sort by empId...");
                for(int i = 0; i < 5; i++)
                {
                    Console.WriteLine(empArray[i]);
                }
                comparer.WhichComparison = Employee.EmployeeComparer.ComparisonType.Yrs;
                empArray.Sort(comparer);
                Console.WriteLine("After sort by Svc...");
                for (int i = 0; i < 5; i++)
                {
                    Console.WriteLine(empArray[i]);
                }
            }
    
        }
    

    image-20210928234318962

    IComparer,Comparer

    Comparer主要用来对有序字典和集合进行排序。需要注意的是,它对无序字典类,如DictionaryHashtable来讲是没有什么用的,这些无序字典类主要还是侧重于hashcode

    public interface IComparer
    {
        int Comparer(object x,object y);
    }
    public interface IComparer<T>
    {
        int Comparer(T x,T,y);
    }
    

    正如equality comparer一样,也有一个可以用来继承的抽象类,而不是实现这些接口:

    public abstract class Comparer<T>:ICompare,ICompare<T>
    {
        public static Comparer<T> Default{get;}
        public abstract int Comparer(T x,T y);//Implemented by you
        int IComparer.Compare(object x,object y);//Implemented for you
    }
    
    • 通过传入Sort()继承Comparer<T>的类来实现排序:
        class Wish
        {
            public string Name;
            public int Priority;
            public Wish(string name,int priority)
            {
                Name = name;Priority = priority;
            }
    
        }
        class PriorityComparer : Comparer<Wish>
        {
            public override int Compare(Wish x, Wish y)
            {
                if (object.Equals(x, y)) return 0;//保证了不与Equals方法冲突,这里object.Equals
                //要比x.Equals方法好,因为它对null仍然有效
                return x.Priority.CompareTo(y.Priority);
            }
        }
        class Program
        {
    
            public static void Main()
            {
                var wishList = new List<Wish>();
                wishList.Add(new Wish("Peace", 2));
                wishList.Add(new Wish("Wealth", 3));
                wishList.Add(new Wish("Love", 2));
                wishList.Add(new Wish("Helth", 1));
                wishList.Add(new Wish("3 more wishes", 1));
    
                wishList.Sort(new PriorityComparer());
                foreach (var w in wishList) Console.Write(w.Name + " | ");
    
    
            }
    
        }
    

    image-20211016155355322

        class SurnameComparer : Comparer<string>
        {
            string Normalize(string s)
            {
                s = s.Trim().ToUpper();
                if (s.StartsWith("MC")) s = "MAC" + s.Substring(2);
                return s;
            }
            public override int Compare(string x, string y)
            {
                return Normalize(x).CompareTo(Normalize(y));
            }
        }
        class Program
        {
    
            public static void Main()
            {
                var dic = new SortedDictionary<string, string>(new SurnameComparer());
                dic.Add("MacPhail", "second");
                dic.Add("MacWilliam", "third");
                dic.Add("McDonald", "first");
                foreach (var s in dic.Values)
                    Console.Write(s + " ");
             }
    
        }
    

    image-20211016172902683

    • 传入Sort()委托j进行排序:
        public class b
        {
            public int x;
            public b(int x)
            {
                this.x = x;
            }
        }
        
        class Program
        {
    
    
            public static void Main()
            {
                var c = new List<b>()
                {
                    new b(10),new b(2),new b(1),new b(3),new b(4)
                };
                c.Sort((l, r) => l.x.CompareTo(r.x));
                foreach (var ele in c) Console.WriteLine(ele.x);
            }
    
        }
    

    image-20211017144741099

    ConvertAll

    List<string> words = new List<string>();
                words.Add("melon");
                words.Add("apple");
                words.AddRange(new[] { "banana", "plum" });//注意list也可以直接加Array,实际上是AddRange(IEnumerable<T> collection)
                words.Insert(0, "lemon");
                words.InsertRange(0, new[] { "peach", "nashi" });
    
                words.Remove("melon");
                words.RemoveAt(3);//去掉第四个元素
                words.RemoveRange(0, 2);//去掉前两个元素
                words.RemoveAll(s => s.StartsWith("n"));
    
                List<string> subset = words.GetRange(1, 2);//得到第二个和第三个元素组成的List<string>
    
                string[] wordsString = words.ToArray(); //得到新的包含其中元素的string[]
    
                string[] existing = new string[1000];
                words.CopyTo(0,existing, 2, 2);//从words的0索引开始,复制到existing的索引2开始,复制2个元素过去
    
                List<string> upperCase = words.ConvertAll(s => s.ToUpper());
                List<int> lengths = words.ConvertAll(s => s.Length);
                Console.WriteLine();
    
    • 创建列表

    使用默认的构造函数创建一个空列表,元素添加到列表后,列表的容量就会扩大为可接纳4个元素,如果添加了第5个元素,列表的大小就重新设置为包含8个元素,如果8个还不够,那么就重新设置为16个元素,每次都会将列表的容量重新设置为原来的2倍。

    为了节省时间,如果事先知道列表中元素的个数,就可以用构造函数定义其容量。

    List<int> intList=new List<int>(10);//创建了一个容量为10个元素的集合,如果不足,则自动重新设置为20,40个元素
    

    ArrayList主要用来保持兼容性,需要臃肿的cast,但它也有优点,那就是它可以混装各种类型的元素,在功能上与List类似。

    var a = new List<int> { 1, 2, 3 };
                var b = new List<string> { "one", "two", "three" };
                var c = new ArrayList() { a, b };
                foreach (var s in c)
                {
                    if(s is List<int>)
                    {
                        var li = s as List<int>;
                        foreach (var p in li)
                            Console.WriteLine(p);
                    }
                    else
                    {
                        var li = s as List<string>;
                        foreach (var p in li)
                            Console.WriteLine(p);
                    }
                    
                }
    

    image-20211010205413386

    LinkedList<T>

    LinkedList<T>是双向列表,它是由一串节点构成的,这些节点记录了它的前一个,后一个节点的引用。它的主要优点是元素可以很高效的插入在列表的任何位置,因为它仅仅包含了创建一个新节点,然后更新少许引用即可。然而,查找特定的位置是比较慢的,因为没有内部机制快速索引一个链表。

    LinkedList<T>实现了IEnumerable<T>ICollection<T>接口,但是没有实现IList<T>接口,因为链表不支持索引。

    			var tune = new LinkedList<string>();
                tune.AddFirst("do");//do
                tune.AddLast("so");//do-so
                tune.AddAfter(tune.First, "re");//do-re-so;
                tune.AddAfter(tune.First.Next, "mi");//do-re-mi-so;
                tune.AddBefore(tune.Last, "fa");//do-re-mi-fa-so;
                tune.RemoveFirst();//re-mi-fa-so
                tune.RemoveLast();//re-mi-fa
    
                LinkedListNode<string> miNode = tune.Find("mi");
                tune.Remove(miNode);//re-fa
                tune.Remove("fa");//re
                tune.AddFirst(miNode);//mi-re
    
        public class Document
        {
            public string Title { get; private set; }
            public string Content { get; private set; }
            public byte Priority { get; private set; }
            public Document(string title,string content,byte priority)
            {
                this.Title = title;
                this.Content = content;
                this.Priority = priority;
            }
        }
        public class PriorityDocumentManager
        {
            private readonly LinkedList<Document> documentList;
            private readonly List<LinkedListNode<Document>> priorityNodes;
            public PriorityDocumentManager()
            {
                documentList = new LinkedList<Document>();
                priorityNodes = new List<LinkedListNode<Document>>(10);//优先级节点列表,列表中是每个优先级最后加入的节点
                for (int i = 0; i < 10; i++)
                {
                    priorityNodes.Add(new LinkedListNode<Document>(null));
                }
            }
            public void AddDocument(Document d)
            {
                if (d == null) throw new ArgumentNullException("d");
                AddDocumentToPriorityNode(d, d.Priority);
            }
            private void AddDocumentToPriorityNode(Document doc,int priority)
            {
                if (priority > 9|| priority < 0)
                    throw new ArgumentException("Priority must be between 0 and 9");
                if (priorityNodes[priority].Value == null)
                {
                    priority--;
                    if (priority >= 0)
                        AddDocumentToPriorityNode(doc, priority);
                    else
                    {
                        documentList.AddLast(doc);//当加入documentList的时候,要同步更改priorityNodes
                        priorityNodes[doc.Priority] = documentList.Last;//当加入documentList的时候,要同步更改priorityNodes
                    }
                }
                else//找到了priorityNode
                {
                    var prioNode = priorityNodes[priority];
                    if (priority == doc.Priority)//该node的优先级与待加入文档的优先级一致
                    {
                        documentList.AddAfter(prioNode, doc);
                        priorityNodes[priority] = prioNode.Next;
                    }
                    else//该node的优先级比加入文档的优先级要低
                    {
                        while (prioNode.Previous!= null && prioNode.Previous.Value.Priority == priority)
                           //第一个判断确保到了prioNode所在优先级的第一个节点
                           //第二个判断确保一直在prioNode所在优先级进行循环
                        {
                            prioNode = prioNode.Previous;
                        }
                        documentList.AddBefore(prioNode, doc);
                        priorityNodes[doc.Priority] = prioNode.Previous;
                    }
    
                }
            }
            public void DisplayAllNode()
            {
                foreach (var doc in documentList)
                    Console.WriteLine("priority:{0},title {1}", doc.Priority, doc.Title);
            }
        }
    
        
        class Program
        {
    
            public static void Main()
            {
                var pdm = new PriorityDocumentManager();
                pdm.AddDocument(new Document("one", "sample", 8));
                pdm.AddDocument(new Document("two", "sample", 3));
                pdm.AddDocument(new Document("three", "sample", 4));
                pdm.AddDocument(new Document("four", "sample", 8));
                pdm.AddDocument(new Document("five", "sample", 1));
                pdm.AddDocument(new Document("six", "sample", 9));
                pdm.AddDocument(new Document("seven", "sample", 1));
                pdm.AddDocument(new Document("eight", "sample", 1));
    
                pdm.DisplayAllNode();
    
            }
    
        }
    

    image-20211016234724530

    Queue<T>

    Queue<T>是先进先出(FIFO)的数据结构,它有Enqueue方法(将元素加入到队列尾部),Deque方法(抽取并移除头部的元素),Peek是获取头部的元素,但不删除,Count属性。

    Queue是可枚举的,但不支持索引,因为没有实现IList接口,ToArray方法负责把元素拷贝到数组中,这样它们就可以被随意的索引。

    var q = new Queue<int>();
                q.Enqueue(10);
                q.Enqueue(20);
                q.Enqueue(30);
                int[] data = q.ToArray();
                Console.WriteLine(q.Count);//2
                Console.WriteLine(q.Peek());//10
                Console.WriteLine(q.Dequeue());//10
                Console.WriteLine(q.Dequeue());//20
                Console.WriteLine(q.Dequeue());
                Console.WriteLine();
    

    image-20211008235440101

    StaCK<T>

    Stack<T>Stack都是后进先出的数据结构,它的方法有Push(在栈顶压入元素),Pop(获取并移除顶部的元素),Peek不对结构做调整,只返回栈顶的元素,Count属性给出栈所包含的元素数量,ToArray给出包含元素的数组,以便可以被随意索引。

    var s = new Stack<int>();
                s.Push(1);//1
                s.Push(2);//1,2
                s.Push(3);//1,2,3
                Console.WriteLine(s.Count);//3
                Console.WriteLine(s.Peek());//3
                Console.WriteLine(s.Pop());//3,此时stack:1,2
                Console.WriteLine(s.Pop());//2,此时stack:1
                Console.WriteLine(s.Pop());//1,此时stack空
                Console.WriteLine(s.Pop());//抛出异常
    

    image-20211009213238287

    BitARRAY

    BitArray是动态尺寸来容纳bool值的数据结构。它比bool数组和泛型bool类型列表更高效,因为它对每个值只用了1 bit,它可以用索引来访问。它有四种方法:And,Or,Xor,Not每个方法的计算结果储存在调用它的bitarray中。

    var bit = new BitArray(2);
                bit[0] = true;
                bit[1] = false;//bit:true,false
                var bit0 = new BitArray(new[] { false, true });//bit0:false,true
                bit.And(bit0);//将结果储存在bit,bit:false,false
                bit.Xor(bit0);//将结果储存在bit,bit:false,true
                 bit.Or(bit0);//将结果储存在bit,bit:false,true
                bit.Not();//将结果储存在bit,bit:true,false
    

    HashSet<T>和SortedSet<T>

    HashSet<T>和SortedSet<T>是Framework 3.5和4.0新增的泛型集合,它们有以下的特点:

    • Contains方法用基于哈希搜寻算法,因此执行的很快。
    • 它们不存储重复的元素,会自动忽略添加的重复的元素
    • 不能根据位置访问元素

    SortedSet<T>按顺序存储元素,而HashSet<T>并不是。

    HashSet<T>用一个哈希表来储存键,SortedSet<T>用红黑树来存储有序元素。

    因为它们都实现了ICollection<T>,所以提供了方法ContainsAddRemove

    下面代码从一个已有的集合,构建了HashSet,测试了元素是否在里面,并且将其枚举出来。

    var letters = new HashSet<char>("the quick brown fox");
                Console.WriteLine(letters.Count);
                Console.WriteLine(letters.Contains('t'));
                Console.WriteLine(letters.Contains('j'));
                foreach (var s in letters) Console.Write("{0},", s);
    
                var ints = new HashSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                Console.WriteLine(ints.Count);
                foreach (var i in ints) Console.Write("{0},",i);
    

    image-20211010101020387

    把字符串传入到HashSet<char>构造器中是因为字符串string是实现了IEnumerable<char>的。

    Set运算符:

    • UnionWith

    pubic void UnionWith(IEnumerable<T> other)

    var letters = new HashSet<char>("the quick brown fox");
                letters.UnionWith("aeiou");
                foreach (var s in letters) Console.Write("{0},", s);
    
                Console.WriteLine();
                var ints = new HashSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                ints.UnionWith(new[] { 100, 200 });
                foreach (var i in ints) Console.Write("{0},",i);
    

    image-20211010104442817

    • IntersectWith

    public void IntersectWith(IEnumeratble<T> other)

    var letters = new HashSet<char>("the quick brown fox");
                letters.IntersectWith("aeiou");
                foreach (var s in letters) Console.Write("{0},", s);
    
                Console.WriteLine();
                var ints = new HashSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                ints.IntersectWith(new[] { 2,4,100, 200 });
                foreach (var i in ints) Console.Write("{0},",i);
    

    image-20211010104929361

    • ExceptWith

    public void ExceptWith(IEnumerable<T> other)

    var letters = new HashSet<char>("the quick brown fox");
                letters.ExceptWith("aeiou");
                foreach (var s in letters) Console.Write("{0},", s);
    
                Console.WriteLine();
                var ints = new HashSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                ints.ExceptWith(new[] { 2,4,100, 200 });
                foreach (var i in ints) Console.Write("{0},",i);
    

    image-20211010105102693

    • SymmetricExceptWith

    public void SymmetricExceptWith(IEnumerable<T>)

    ExceptWith不同的是,前者是从source set中移除传入方法中的集合的所有元素。而SymmetricExceptWith是使得source set只包含参数集合中和自己本身唯一的元素,而去掉两边都有的,这意味着如果参数集合中有元素在source set中不存在,那么source set将添加该元素。

    var letters = new HashSet<char>("the quick brown fox");
                letters.SymmetricExceptWith("aaeiiouu");
                foreach (var s in letters) Console.Write("{0},", s);
    
                Console.WriteLine();
                var ints = new HashSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                ints.SymmetricExceptWith(new[] { 2,2,4,100, 200 });
                foreach (var i in ints) Console.Write("{0},",i);
    

    image-20211010111830087

    SortedSet

    SortedSet提供所有HashSet<T>的成员,但它还有自己独有的成员:

    public virtual SortedSet<T> GetViewBetween(T lowerValue,T upperValue);
    public IEnumerable<T> Reverse();
    public T Min{get;}
    public T Max{get;}
    

    例子:

    var letters = new SortedSet<char>("the quick brown fox");
                Console.WriteLine(letters.Max);
                Console.WriteLine(letters.Min);
                foreach (var s in letters.GetViewBetween('f','j')) Console.Write("{0},", s);
    
                Console.WriteLine();
                var ints = new SortedSet<int>(new[] { 1, 2, 3, 2, 1, 4, 3, 4, 5 });
                Console.WriteLine(ints.Min);
                Console.WriteLine(ints.Max);
                foreach (var i in ints.GetViewBetween(2,4)) Console.Write("{0},",i);
    

    image-20211010113511667

    Dictionaries

    类型 内部结构 能否通过索引访问
    无序
    Dictionary<K,V> Hashtable No
    Hashtable Hashtable No
    ListDictionary Linked list No
    OrderedDictionary Hashtable+arry Yes
    有序
    SortedDictionary<K,V> Red/Black tree No
    SortedList<K,V> 2xArray Yes
    SortedList 2xArray Yes

    用Big-O的角度,通过键,访问元素的时间为:

    • O(1):Hashtable,Dictionary,OrderedDictionary
    • O(logn):SortedDictionary, SortedList
    • O(n):ListDictionary

    IDictionary<TKey,TValue>

    IDictionary<Tkey,Tvalue>给所有键-值对集合提供了标准协议,它通过增加基于键来访问值的方法拓展了ICollection<T>

    public interface IDictionary<TKey,TValue>:
    ICollection<KeyvaluePari<TKey,TValue>>,IEnumerable
    {
        bool ContainsKey(TKey);
        bool TryGetValue(TKey key,out TValue value);
        void Add(TKey,TValue);
        bool Remove(TKey key);
        
        TValue this[TKey key]{get;set;} //通过键来索引
        ICollection<TKey> Keys{get;} //只读属性,得到key组合形成的集合
        ICollection<TValue> Values{get;}//只读属性,得到Value组合形成的集合
        
    }
    

    从Framework 4.5起,有一个接口IReadOnlyDictionary<TKey,TValue>,它定义了字典成员的 只读子集。

    遍历IDictionary<TKey,TValue会返回KeyValuePair结构序列

    public struct KeyValuePair<TKey,TValue>
    {
        public TKey Key{get;}
        public TValue Value{get;}
    }
    

    IDictionary

    非泛型IDictionary接口与IDictionary<TKey,TValue>大致相同,除了以下几点:

    • 试图通过索引来获取不存在的键,会返回Null,而非抛出异常

    • Contains来检验成员而非ContainsKey

                  var dict = new Hashtable();
                  dict.Add(1, "one");
                  dict.Add(2, "two");
                  dict.Add("three", 3);
                  dict[2] = "twotow";
                  Console.WriteLine(dict.Contains(2));
                  Console.WriteLine(dict.Contains("three"));
                  Console.WriteLine(dict.Contains(3));
                  var a = dict[3];
                  Console.WriteLine(a == null);
                  Console.WriteLine((int)dict["three"]);
      

      image-20211010172920603

    遍历非泛型IDictionary返回DictionaryEntry结构序列:

    public struct DictionaryEntry
    {
        public object Key{get;}
        public object Value{get;}
    }
    

    Dictionary<TKey,TValue>和Hashtable

    Dictionary<TKey,TValue>的非泛型版本就称为Hashtable,没有叫Dictionary的非泛型类型(所以没有new Dictionary())。

                var dict = new Dictionary<string, int>();
                dict.Add("one", 1);
                dict["two"] = 2;
                dict["two"] = 22;//更新two
                dict["Three"] = 3;
                Console.WriteLine(dict["two"]);
                Console.WriteLine(dict.Contains(new KeyValuePair<string, int>("one", 2)));
                Console.WriteLine(dict.ContainsKey("two"));
                Console.WriteLine(dict.ContainsValue(100));
    
                int value = 0;
                if(!dict.TryGetValue("OnE",out value))
                {
                    Console.WriteLine("No OnE");
                }
    
                if(dict.TryGetValue("Three",out value))
                {
                    Console.WriteLine("Three is {0}", value);
                }
    
                foreach(KeyValuePair<string,int> kv in dict)
                {
                    Console.WriteLine(kv.Key + ":" + kv.Value);
                }
    
                foreach (string s in dict.Keys)
                    Console.WriteLine(s);
                Console.WriteLine();
                foreach (int i in dict.Values)
                    Console.WriteLine(i);
    

    image-20211010161836136

    底层的hashtable是这样工作的:把每个元素的键转换为整数的hashcode,伪唯一值(pseudo unique value),然后将之施加到特定的算法求得hash key。这个hash key就是内部用来确定entry属于哪个bucket的,如果bucket包含了不止一个值,线性搜索就在bucket上进行了。

    字典可以将任何类型作为键,只要它能保证键之间的相等性,和获取hashcode。

    默认地,相等性是通过键的object.Equals方法,pseudo unique hashcode是通过键的GetHashCode方法获取的。

    通过键来获取值,或者查找存不存在某些键,可以被认为是首先通过被查找的对象的GetHashCode得到hash code,然后转换为hash key,从bucket取出存储的key对象,然后通过检查被查找的键与存储的键是否相等,来判断是否找到了值,或存不存在该查找的键

        class Foo:IEquatable<Foo>
        { public int x;
            public int y;
            public override bool Equals(object obj)//重写Equals算法,注意这里参数是object
            {
                if (obj == null)
                    return base.Equals(obj);//base.Equal是
                return Equals(obj as Foo);
            }
            public bool Equals(Foo other) //实现IEquatable接口,注意这里参数是Foo类型
            {
                if (other == null)
                    return base.Equals(other);
                return this.y == other.y;
            }
            public override int GetHashCode()
            {
                return this.x.GetHashCode();
            }
    
        }
    
     public static void Main()
            {
    
                var f1 = new Foo { x = 5,y=4};
                var f2 = new Foo { x = 3,y=2 };
                var f3 = new Foo { x = 5,y=5 };
                var flist = new List<Foo>();
                flist.Add(f1);
                flist.Add(f2);
                flist.Add(f3);
                Console.WriteLine(f1.Equals(f3));
                Console.WriteLine(flist.Contains(f3));
                Console.WriteLine(flist.Distinct().Count());
                var dic = new Dictionary<Foo, string>();
                dic.Add(f1, "f1");
                dic.Add(f2, "f2");
                Console.WriteLine(dic[f3]);
    
            }
    

    image-20211010171100345

    上面例子表明:f3与f1不相等,虽然它们在字典中的hashcode一样,但不相等,所以取不到f3对应的值。

    下面将Foo的相等性改为判断其x值是否相等:

        class Foo:IEquatable<Foo>
        { public int x;
            public int y;
            public override bool Equals(object obj)//重写Equals算法,注意这里参数是object
            {
                if (obj == null)
                    return base.Equals(obj);//base.Equal是
                return Equals(obj as Foo);
            }
            public bool Equals(Foo other) //实现IEquatable接口,注意这里参数是Foo类型
            {
                if (other == null)
                    return base.Equals(other);
                return this.x == other.x;
            }
            public override int GetHashCode()
            {
                return this.x.GetHashCode();
            }
    
        }
    
     public static void Main()
            {
    
                var f1 = new Foo { x = 5,y=4};
                var f2 = new Foo { x = 3,y=2 };
                var f3 = new Foo { x = 5,y=5 };
                var flist = new List<Foo>();
                flist.Add(f1);
                flist.Add(f2);
                flist.Add(f3);
                Console.WriteLine(f1.Equals(f3));
                Console.WriteLine(flist.Contains(f3));
                Console.WriteLine(flist.Distinct().Count());
                var dic = new Dictionary<Foo, string>();
                dic.Add(f1, "f1");
                dic.Add(f2, "f2");
                Console.WriteLine(dic[f3]);
    
            }
    

    image-20211010171204727

    改了Foo的相等性,可以发现List的Distinct方法认为f3与f1相等,所以给出了Distinct().Count()为2的结果,而字典也可以顺利找到f3对应的值。

    C# 相等比较 - JohnYang819 - 博客园 (cnblogs.com)中的例子(注释掉Foo的GetHashCode方法),表明就算f3与f1相等,如果hash code不一样,还是取不了f3对应的值,因为在bucket中查找是按照hash code存放的,现在hash code 不一样,所以根本查不到。

    OrderedDictionary

    OrderedDictionary是非泛型字典,它保持了元素被加进来的顺序,它不是有序字典,可以通过键或者索引来访问它。

    Sorted Dictionaries

    Framework提供了两类字典,它们的键总是有序的:

    • SortedDictionary<Tkey,TValue>
    • SortedList<Tkey,TValue>.
      var sorted = new SortedList<string, MethodInfo>();
                foreach (var m in typeof(object).GetMethods())
                    sorted[m.Name] = m;
                foreach (var name in sorted.Keys)
                    Console.WriteLine(name);
                Console.WriteLine();
                foreach (var m in sorted.Values)
                    Console.WriteLine(m.Name + "returns:  " + m.ReturnType);
                Console.WriteLine(sorted.Keys[sorted.Count - 1]);
                Console.WriteLine(sorted["GetHashCode"]);
            }
    

    image-20211010225701143

    ReadOnlyCollection<T>

    Read-only collection接受输入集合在构造函数,它维护了永久的引用,没有静态拷贝输入集合,所以后续对输入集合的改变都是可见的。

    比如,假如想构造一个类,该类提供一个只读的字符串列表Names:

    public class Test{
        public List<string> Names{get;private set;}
    }
    

    这仅仅完成了一半的工作,尽管其他类型不能对Names属性进行赋值,但它们还可以Add,Remove,Cleear,而ReadOnlyCollection<T>类就可以解决这些问题:

        public class Test
        {
            List<string> names;
            public ReadOnlyCollection<string> Names { get; private set; }
            public Test()
            {
                names = new List<string>();
                Names = new ReadOnlyCollection<string>(names);
            }
            public void AddInternally() { names.Add("test"); }
        }
    
        class Program
        {
    
            public static void Main()
            {
                Test t = new Test();
                Console.WriteLine(t.Names.Count);
                t.AddInternally();
                Console.WriteLine(t.Names.Count);
                t.Names.add("test");
    
            }
    
        }
    

    image-20211014234110785

    去掉最后一行:

    image-20211014234221462

    IEqualityComparerEqualityComparer

    相等比较(equality comparer)可以处理非默认情况下的相等和哈希行为,主要为了DictionaryHashtable

    回忆下基于哈希的字典,它有两个问题要回答:

    • 它是否与另一个一样
    • 它的hashcode是什么

    相等比较(equality comparer)通过实现IEqualityComparer接口回答了这两个问题。

    public interface IEqualityComparer<T> //泛型
    {
        bool Equals(T x,T y);
        int GetHashCode(T obj);
    }
    
    public interface IEqualityComparer //非泛型
    {
        bool Equality(object x,object y);
        int GetHashCode(object obj);
    }
    

    为了写出典型的比较器(custom comparer),必须实现一个或者两个接口,因为这很繁琐,所以一个代替的方案是直接从抽象类EqualityComparer派生类,该抽象类定义如下:

    public abstract class EqualityComparer<T>:IEqualityCompaer,IEqualityComparer<T>
    {
        public abstract bool Equals(T x,T y);//非抽象派生类必须实现
        public abstract int GetHashCode(T obj);//非抽象派生类必须实现
        
        bool IEqualityComparer.Equals(object x,object y);//非泛型接口自己的方法
        int IEqualityComparer.GetHashCode(object obj);//非泛型接口自己的方法
        
        public static EqualityComparer<T> Default{get;}
    }
    

    抽象类EqualityComparer实现了两个接口,所以我们的任务就是覆写最上面两个抽象方法。

        public class Customer
        {
            public string LastName;
            public string FirstName;
            public Customer(string last,string first)
            {
                LastName = last;FirstName = first;
            }
        }
        public class LastFirstEqualityComparer : EqualityComparer<Customer>
        {
            public override bool Equals(Customer x, Customer y)
                => (x.LastName == y.LastName && x.FirstName == y.FirstName);
            public override int GetHashCode(Customer obj)
                => (obj.LastName + ";" + obj.FirstName).GetHashCode();
        }
    
        class Program
        {
    
            public static void Main()
            {
                var c1 = new Customer("John", "Yang");
                var c2 = new Customer("John", "Yang");
                Console.WriteLine(c1 == c2);//false
                Console.WriteLine(c1.Equals(c2));//false,因为我们没有覆写object.Equals
    
                var d = new Dictionary<Customer, string>();
                d[c1] = "joe";
                Console.WriteLine(d.ContainsKey(c2));//false,因为Customer没有覆写Equals和GetHashCode
    
                var eqComparer = new LastFirstEqualityComparer();
                var dd = new Dictionary<Customer, string>(eqComparer);
                dd[c1] = "joe";
                Console.WriteLine(dd.ContainsKey(c2));//true,虽然Customer没有覆写Equals和GetHashCode,但传入了EqualityComparer类
                //所以plugging in Equlity就是这个意思
                eqComparer.Equals(c1, c2);
    
            }
    
        }
    

    IstructuralEquatableIStructuralComparable

        struct test
        {
            int a;
            int b;
            public test(int a,int b)
            {
                this.a = a;
                this.b = b;
            }
        }
    
        
        class Program
        {
    
            public static void Main()
            {
                int[] a1 = { 1, 2, 3 };
                var a2 = new int[] { 1, 2, 3 };
                Console.WriteLine(a1.Equals(a2));
                var se1 = a1 as IStructuralEquatable;
                Console.WriteLine(se1.Equals(a2, EqualityComparer<int>.Default));
                var t = new test(1,2);
                var t1 = new test(1, 2);
                Console.WriteLine(t.Equals(t1));
             }
    
        }
    

    image-20211016212433796

    Lookup

    Dictionary<TKey,TValue>类支持每个键关联一个值,而Lookup<TKey,TValue>则非常类似于前者,但把键映射到一个值集上。这个类在程序集System.Core实现,用System.Linq名称空间定义。

    Lookup<TKey,TValue>类不能像一般的字典那样创建,而必须调用ToLookup()方法,该方法返回一个Lookup<TKey,TElement>对象。

    Tolookup()方法是一个扩展方法,它可以用于实现了IEnumerable<T>接口的所有类。

        public class Racer
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string Country { get; set; }
            public int Wins { get; set; }
            public Racer(string firstName,string lastName,string country,int wins)
            {
                this.FirstName = firstName;this.LastName = lastName;
                this.Country = country;this.Wins = wins;
            }
            public Racer(string firstName,string lastName,string country)
                : this( firstName, lastName, country, wins:0) { }
        }
    
        
        class Program
        {
            public static void Main()
            {
                var racers = new List<Racer>
                {
                    new Racer("Jacques","Villeneve","Canada",11),
                    new Racer("Wei","Zhang","China",20),
                    new Racer("Alasn","Joe","Australia",10),
                    new Racer("Xiao","Li","China",29),
                    new Racer("Jack","Baram","America",14)
                };
                var lookups = racers.ToLookup(r => r.Country);
                foreach (var r in lookups["China"]) Console.WriteLine(r.FirstName + r.LastName);
            }
    
        }
    

    image-20211017162926584

    传入ToLookup方法的实际上是Func<TSource,TKey>委托,这里TSourceRacerTKey是string。

    ObservableCollection<T>

    如果需要集合中的元素何时删除或添加的信息,就可以使用ObservableCollection<T>类。这个类是为WPF定义的,这样UI就可以得知集合的变化,因此这个类在程序集WindowBase中定义,这个类的命名空间是System.Collections.ObjectModel

    ObservableCollection<T>派生自Collection<T>基类,它重写基类中的虚方法SetItem()RemoveItem(),以触发CollectionChanged事件,这个类的用户可以使用INotifyCollectionChanged接口注册这个事件。

     class Program
        {
            public static void Main()
            {
                var data = new ObservableCollection<string>();
                data.CollectionChanged += Data_CollectionChanged;
                data.Add("one");
                data.Add("two");
                data.Insert(1, "Three");
                data.Remove("one");
                data.Clear();
          
            }
            public static void Data_CollectionChanged(object sender,NotifyCollectionChangedEventArgs e)
            {
                Console.WriteLine("action:{0}", e.Action.ToString());
                if (e.OldItems != null)
                {
                    Console.WriteLine("starting index for old items:{0}", e.OldStartingIndex);
                    Console.WriteLine("old items:");
                    foreach (var item in e.OldItems)
                    {
                        Console.WriteLine(item);
                    }
                    Console.WriteLine();
                }
                if (e.NewItems != null)
                {
                    Console.WriteLine("starting index for new items:{0}", e.NewStartingIndex);
                    Console.WriteLine("new items:");
                    foreach (var item in e.NewItems)
                        Console.WriteLine(item);
                    Console.WriteLine();
                }
            }
        }
    

    image-20211017191344065

    Data_collectionChanged()方法接受NotifyCollectionChangedEventArgs,其中包含了集合的变化信息,Action给出了是否添加或删除一项的信息,对于删除的项,会设置OldItems属性,列出删除的项,对于添加的项,则设置NewItems属性,列出新增的项。

    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第三节 梯度下降法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第二章 pandas 第五节 Getting Started with pandas
    Oracle数据库安装和授权
    c# 如何获取JSON文件以及如何获取Config文件(framework 和 net .Core)
    C#Core查询数据库存储EXCEL文件
    如何在WINDOW系统下编译P12证书制作
  • 原文地址:https://www.cnblogs.com/johnyang/p/15417804.html
Copyright © 2011-2022 走看看