• 《C#本质论》读书笔记(16)构建自定义集合


    16.1 更多集合接口

    集合类(这里指IEnumerable层次结构)实现的接口层次结构



    16.1.1 IList<T>与IDictionary<TKey,TValue>

    字典类一般只按照键进行索引,而不按位置索引。
    列表“键”总是一个整数,“键集”总是从0开始的非负整数的一个连续集合。
    解决数据存储或数据获取问题时,考虑  IList<T>(侧重位置索引获取值)与 IDictionary<TKey,TValue>(侧重通过键来获取值)。

    16.1.2 ICompatable<T>

    ICompatable<T>是实现排序的关键。
    例如, List<T>.Sort() ,你需要采取某种方式比较对象,一个办法就是使用 ICompatable<T> 接口。该接口有一个 CompareTo() 方法,返回一个整数,指出传递元素是大于、小于还是等于当前元素,所以,键的数据类型必须实现 ICompatable<T> 


    高级主题:用IComparer<T>排序

          为了实现自定义排序,另一个方法是向排序方法传递实现了 IComparer<T> 的元素。集合中的元素通常不支持这个接口。
        IComparable<T>  IComparer<T> 的区别很细微,但重要。
    IComparable<T> 说:“我知道如何将自己和我的类型的另一个实例进行比较
    IComparer<T> 说:“我知道如何比较给定类型的两个实例

    1. public static void Main()    

    2. {    

    3.     Contact aaa = new Contact() { LastName = "bbb", FirstName = "ddd" };    

    4.     Contact bbb = new Contact() { LastName = "aaa", FirstName = "ccc" };    

    5.     

    6.     //Console.WriteLine(new NameComparison().Compare(aaa, bbb));    

    7.     

    8.     List<Contact> contactlist = new List<Contact>();    

    9.     contactlist.Add(aaa);    

    10.     contactlist.Add(bbb);    

    11.     

    12.     foreach (var contact in contactlist)    

    13.     {    

    14.         Console.WriteLine(contact.LastName + " ");    

    15.     }    

    16.     //排序    

    17.     contactlist.Sort(new NameComparison());    

    18.     

    19.     foreach (var contact in contactlist)    

    20.     {    

    21.         Console.WriteLine(contact.LastName + " ");    

    22.     }    

    23.     

    24.     Console.Read();    

    25.     

    26. }    

    27.     

    28. class Contact    

    29. {    

    30.     public string FirstName { getset; }    

    31.     public string LastName { getset; }    

    32. }    

    33.     

    34.     

    35. class NameComparison : IComparer<Contact>    

    36. {    

    37.     public int Compare(Contact x, Contact y)    

    38.     {    

    39.         int result;    

    40.     

    41.         if (Contact.ReferenceEquals(x, y))    

    42.         {    

    43.             result = 0;    

    44.         }    

    45.         else    

    46.         {    

    47.             if (x == null)    

    48.             {    

    49.                 result = 1;    

    50.             }    

    51.             else if (y == null)    

    52.             {    

    53.                 result = -1;    

    54.             }    

    55.             else    

    56.             {    

    57.                 result = StringCompare(x.LastName, y.LastName);    

    58.                 if (result == 0)    

    59.                 {    

    60.                     result =    

    61.                         StringCompare(x.FirstName, y.FirstName);    

    62.                 }    

    63.             }    

    64.         }    

    65.         return result;    

    66.     }    

    67.     

    68.     private static int StringCompare(string x, string y)    

    69.     {    

    70.         int result;    

    71.         if (x == null)    

    72.         {    

    73.             if (y == null)    

    74.             {    

    75.                 result = 0;    

    76.             }    

    77.             else    

    78.             {    

    79.                 result = 1;    

    80.             }    

    81.         }    

    82.         else    

    83.         {    

    84.             result = x.CompareTo(y);    

    85.         }    

    86.         return result;    

    87.     }    

    88. }    


    16.2 主要集合类

    共有5类关键的集合类,所有泛型类都位于 System.Collections.Generic命名空间。

    16.2.1 列表集合:List<T>

    List<T>具有与数组相似的属性。关键在于会自动的扩展,也可以显示调用 TrimToSize() 或 Capacity 来缩小。

    1. List<string> list = new List<string>();  

    2.      

    3. // Lists automatically expand as elements  

    4. // are added.  

    5. list.Add("Sneezy");  

    6. list.Add("Happy");  

    7. list.Add("Dopey");  

    8. list.Add("Doc");  

    9. list.Add("Sleepy");  

    10. list.Add("Bashful");  

    11. list.Add("Grumpy");  

    12.      

    13. list.Sort();  

    14.      

    15. Console.WriteLine(  

    16.     "In alphabetical order {0} is the "  

    17.     + "first dwarf while {1} is the last.",  

    18.     list[0], list[6]);  

    19.      

    20. list.Remove("Grumpy");  


    16.2.3 搜索List<T>

    要在 List<T> 查找特定的元素,可以使用 Contains()  IndexOf()  LastIndexOf()  BinarySerch() 方法。
       BinarySerch() 采用的是快得多的二分搜索算法,但要求元素已经排序好。一个有用的功能是假如元素没找到,会返回一个负整数。该值的按位取反(~)结果是”大于被查找元素的下一个元素“的索引,如果没有更大的值,则是元素的总数。这样就可以在特定位置方便插入新值。
    1. List<string> list = new List<string>();  

    2. int search;  

    3.   

    4. list.Add("public");  

    5. list.Add("protected");           

    6. list.Add("private");  

    7.   

    8.   

    9. list.Sort();  

    10.   

    11. search = list.BinarySearch("protected internal");  

    12. if (search < 0)  

    13. {  

    14.     list.Insert(~search, "protected internal");  

    15. }  

    16.   

    17. foreach (string accessModifier in list)  

    18. {  

    19.     Console.WriteLine(accessModifier);  

    20. }  


    高级主题:使用 FindAll() 查找多个数据项

       FindAll() 获取 Predicate<T> 类型的一个参数,它是对称为“委托”的一个方法的引用
    1. public static void Main()  

    2. {  

    3.     List<int> list = new List<int>();  

    4.     list.Add(1);  

    5.     list.Add(2);  

    6.     list.Add(3);  

    7.     list.Add(2);  

    8.     list.Add(4);  

    9.   

    10.     List<int> results = list.FindAll(Even);  

    11.   

    12.     foreach (int number in results)  

    13.     {  

    14.         Console.WriteLine(number);  

    15.     }  

    16.     //2,2,4  

    17.     Console.Read();  

    18.   

    19. }  

    20.   

    21. public static bool Even(int value)  

    22. {  

    23.     return (value % 2) == 0;  

    24. }  


    传递一个委托实例 Even() 。若整数实参值是偶数,就返回 true 。

    16.2.4 字典集合:Dictonary<TKey,TValue>

    和列表集合不同,字典类存储是“名称/值”对。
     插入元素,一个选择是使用 Add() 方法。


    1. Dictionary<Guid, string> dictionary =  

    2. new Dictionary<Guid, string>();  

    3. Guid key = Guid.NewGuid();  

    4.   

    5. dictionary.Add(key, "object");  


    还有个选择是索引操作符

    1. Dictionary<Guid, string> dictionary =  

    2.    new Dictionary<Guid, string>();  

    3. Guid key = Guid.NewGuid();  

    4.   

    5. dictionary[key] = "object";  

    6. dictionary[key] = "byte";  


    由于键和值都要添加到字典中,所以用于枚举字典中的元素的 foreach 循环的循环变量必须是 KeyValuePair<TKey,TValue> 

    1. Dictionary<stringstring> dictionary = new  

    2.    Dictionary<stringstring>();  

    3.   

    4. int index = 0;  

    5.   

    6. dictionary.Add(index++.ToString(), "object");  

    7. dictionary.Add(index++.ToString(), "byte");  

    8. dictionary.Add(index++.ToString(), "uint");  

    9. dictionary.Add(index++.ToString(), "ulong");  

    10. dictionary.Add(index++.ToString(), "float");  

    11. dictionary.Add(index++.ToString(), "char");  

    12. dictionary.Add(index++.ToString(), "bool");  

    13. dictionary.Add(index++.ToString(), "ushort");  

    14. dictionary.Add(index++.ToString(), "decimal");  

    15. dictionary.Add(index++.ToString(), "int");  

    16. dictionary.Add(index++.ToString(), "sbyte");  

    17. dictionary.Add(index++.ToString(), "short");  

    18. dictionary.Add(index++.ToString(), "long");  

    19. dictionary.Add(index++.ToString(), "void");  

    20. dictionary.Add(index++.ToString(), "double");  

    21. dictionary.Add(index++.ToString(), "string");  

    22.   

    23. Console.WriteLine("Key  Value    Hashcode");  

    24. Console.WriteLine("---  -------  --------");  

    25. foreach (KeyValuePair<stringstring> i in dictionary)  

    26. {  

    27.     Console.WriteLine("{0,-5}{1,-9}{2}",  

    28.         i.Key, i.Value, i.Key.GetHashCode());  

    29. }  

     如果只处理字典中的键或值,那么可以用 Keys  Values 属性。这些属性返回的数据类型是 ICollection<T> 。他们返回的是对原始字典集合中的数据的引用,而不是返回的副本。


    16.2.5 已排序集合:SortedDictionary<TKey,TValue>和SortedList<T>

    已排序集合类的元素是已经排好序的。具体说对于 SortedDictionary<TKey,TValye> 元素是按照键排序的;对于 SortedList<T> ,元素是按照值排序的(还有一个非泛型的  SortedList 实现)。

    1. SortedDictionary<stringstring> sortedDictionary =  

    2.    new SortedDictionary<stringstring>();  

    3.   

    4. int index = 0;  

    5.   

    6. sortedDictionary.Add(index++.ToString(), "object");  

    7. sortedDictionary.Add(index++.ToString(), "byte");  

    8. sortedDictionary.Add(index++.ToString(), "uint");  

    9. sortedDictionary.Add(index++.ToString(), "ulong");  

    10. sortedDictionary.Add(index++.ToString(), "float");  

    11. sortedDictionary.Add(index++.ToString(), "char");  

    12. sortedDictionary.Add(index++.ToString(), "bool");  

    13. sortedDictionary.Add(index++.ToString(), "ushort");  

    14. sortedDictionary.Add(index++.ToString(), "decimal");  

    15. sortedDictionary.Add(index++.ToString(), "int");  

    16. sortedDictionary.Add(index++.ToString(), "sbyte");  

    17. sortedDictionary.Add(index++.ToString(), "short");  

    18. sortedDictionary.Add(index++.ToString(), "long");  

    19. sortedDictionary.Add(index++.ToString(), "void");  

    20. sortedDictionary.Add(index++.ToString(), "double");  

    21. sortedDictionary.Add(index++.ToString(), "string");  

    22.   

    23. Console.WriteLine("Key  Value    Hashcode");  

    24. Console.WriteLine("---  -------  ----------");  

    25. foreach (  

    26.     KeyValuePair<stringstring> i in sortedDictionary)  

    27. {  

    28.     Console.WriteLine("{0,-5}{1,-9}{2}",  

    29.         i.Key, i.Value, i.Key.GetHashCode());  

    30. }  


     键中元素采用的是字幕顺序,而不是数值顺序,这是由于键是字符串,而不是整数。
    在一个已排序的字典集合中插入或删除元素时,由于要保持集合中的元素顺序,所以相对前面的 Dictionary<TKey,TValue> 执行事件要稍微长一些。它内部使用两个数组,一个用于键的检索,一个勇于索引的检索。
    对于 System.Collections.Sorted 排序列表,索引操作是通过 GetByIndex()  SetByIndex() 方法来支持的。
    对于 System.Collections.Generic.SortedList<TKey,TValue> Keys  Values 属性分别返回 IList<TKey>  IList<TValue> 实例。这些方法使得已排序列表具有字典行为,也有列表类型的集合的行为。

    16.2.6 栈集合:Stack<T>

    栈集合被设计为:后进先出
    两个方法:
    Push():插入元素。
    Pop():按照与添加时相反的顺序获取并删除元素。
     为了不修改栈的前提下访问栈中的元素,使用 Peek()  Contains() 方法。
     Peek() :返回 Pop() 将获取的下一个元素。
     Contains() :勇于判断一个元素是否存在于栈的某个地方。

    16.2.7队列集合:Queue<T>

    队列集合类和栈集合类基本相同,遵循先入先出模式
     Enqueue() 入队
     Dequeue() :出队。
    队列集合根据需要自动增大。但缩小时不一定回收之前使用的存储空间,因为这会使插入新元素的动作变得很昂贵。如果确定队列长时间大小不变,可以使用 TrimToSize() 方法提醒队列集合你希望回收存储空间。

    16.2.8 链表:LinkedList<T>

     链表允许正向和反向遍历。(没有对应的非泛型类型)

    16.4 返回null或者空集合

    返回数组或集合时,必须允许返回 null ,或者返回不包含任何数据的集合实例。
    通常,返回没有数据的集合实例是更好的选择,可避免遍历集合数据前检查 null 值。
    但是这个准则也有例外,比如 null 被有意的用来表示有别于“零个项目”的情况。
    例如,网站用户名集合可能会是 null ,以此来表示出于某种原因未获得最新集合。

    16.5 迭代器

    本节讨论利用迭代器为自定义集合实现自己的 IEnumerator<T>  IEnumerable<T> 和对应的非泛型接口。
       加入某个类希望支持 foreach 进行迭代,就必须实现枚举器( enumerator )模式。

    16.5.1 迭代器定义

    迭代器是实现类的方法的一个途径,是更加复杂的枚举数模式的语法简化形式。由于在生成的CIL代码中仍然采用枚举数模式,所以并不会带来真正的运行时性能优势。不过,使用迭代器而不是手动实现枚举数模式,能显著提高程序员的编程效率。

    16.5.2 迭代器语法

    迭代器提供了迭代器接口(也就是 IEnumerator<T>  IEnumerable<T> 的组合)的一个快捷实现。
    创建一个 GetEnumerator() 方法,表示声明了一个迭代器。接着要添加对迭代器接口的支持
    1. public IEnumerator<T> GetEnumerator()  

    2. {  

    3.         //...  

    4.         return new List<T>.Enumerator();//This will be implimented in 16.16  

    5. }  


    16.1.3 从迭代器生成值

    迭代器类似于函数,但它不返(renturn)值,而是生成(yield)一系列值。
    未完待续。。。









  • 相关阅读:
    .net core 在 Docker 上的部署
    js 运算的内置函数
    vux 项目的构建
    微信小程序开发资料
    HttpClient 调用WebAPI时,传参的三种方式
    jsplumb 中文教程
    MyEclipse最新版-版本更新说明及下载
    如何用VSCode调试Vue.js
    vs2017开发Node.js控制台程序
    Objc的底层并发API
  • 原文地址:https://www.cnblogs.com/tangge/p/6088257.html
Copyright © 2020-2023  润新知