.NET 基础-3

特性

数据契约

简介:

  • 服务端和客户端之间要传送的自定义数据类型
  • 当应用于类型的成员时,指定该成员是数据协定的一部分并可由 DataContractSerializer 进行序列化。
  • 特性应用到的属性必须同时具有 get 和 set 字段
  • 由成员返回的数据(即使是私有的)会进行序列化和反序列化,因此恶意用户或进程可以查看或截获这些数据。

例子

Types:
[DataContract]
public class File
{
    // excluded from serialization
    // does not have DataMemberAttribute
    public Guid Id { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int Size { get; set; }
}
Usage:
File file = new File
{
    Id = Guid.NewGuid(),
    Name = "ImportantLegalDocuments.docx",
    Size = 50 * 1024
};

string json = JsonConvert.SerializeObject(file, Formatting.Indented);

Console.WriteLine(json);
// {
//   "Name": "ImportantLegalDocuments.docx",
//   "Size": 51200
// }
还可以转换为字典类型
 SortedDictionary<string, string> dicts = JsonConvert.DeserializeObject<SortedDictionary<string, string>>(JsonModel);

集合的选型

简介

  • 开发过程中,我们都知道java对于集合总是使用hashmap。我在平常开发过程中一般都是list等,那么关于集合我们怎么选择呢?类似的数据在作为集合而存储和操作时通常可以得到更高效地处理。

命名空间

  • system.Array
  • System.Collections
  • System.Collections.Generic
  • System.Collections.Concurrent
  • System.Collections.lmmutable

以上几个集合的命名空间类主要作用这里简单介绍一下,与上面一一对应

  • 用于创建、处理、搜索数组并对数组进行排序,从而充当公共语言运行时中所有数组的基类
  • 接口和类定义各种对象(如列表、队列、位数组、哈希表和字典)的集合。
  • 定义泛型集合的接口和类,用户可以使用泛型集合来创建强类型集合,这种集合能提供比非泛型强类型集合更好的类型安全性和性能。
  • 提供多个线程安全集合类。当有多个线程并发访问集合时,应使用这些类代替 System.Collections 和 System.Collections.Generic 命名空间中的对应类型。 但是,不保证通过扩展方法或通过显式接口实现访问集合对象是线程安全的,可能需要由调用方进行同步
  • 包含用于定义不可变集合的接口和类。

常用集合功能

实现集合的操作。可以直接或间接实现 ICollection 接口或 ICollection 接口,这些接口的集合均共享这些功能:

  • 可枚举集合
    将枚举器看作集合中可指向任何元素的可移动指针,可以使用
  • 可将集合内容复制到数组
    得到的数组始终是一维的,下限为零
  • 容量和计数属性
    当达到当前容量时,大多数的集合都会自动扩展容量。具体操作:重新分配内存并将元素从旧的集合复制都新的集合,这减少了要求使用集合的代码;性能可能会收到不利影响。例如,对 List 来说,如果 Count 比 Capacity 少,那么添加项就是一项 O(1) 操作。 如需增加容量以容纳新元素,则添加项成为 O(n) 操作,其中 n 是 Count。 避免因多次重新分配而导致的性能较差的最佳方式是:将初始容量设置为集合的估计大小。
    BitArray 是一种特殊情况;它的容量与其长度相同,而其长度与其计数相同。
  • 下限一致
  • 同步以从多个线程进行访问(仅 System.Collections 类)

集合复杂度

可变 复杂度最优 复杂度最坏 不可变 复杂度
Stack.Push O(1) O(n) ImmutableStack.Push O(1)
Queue.Enqueue O(1) O(n) ImmutableQueue.Enqueue O(1)
List.Add O(1) O(n) ImmutableList.Add O(log n)
List.Item[Int32] O(1) O(1) ImmutableList.Item[Int32] O(log n)
List.Enumerator O(n) O(n) ImmutableList.Enumerator O(n)
HashSet.Add, lookup O(1) O(n) ImmutableHashSet.Add O(log n)
SortedSet.Add O(log n) O(n) ImmutableSortedSet.Add O(log n)
Dictionary.Add O(1) O(n) ImmutableDictionary.Add O(log n)
Dictionary lookup O(1) O(1) -或者从严格意义上说,O(n) ImmutableDictionary lookup O(log n)
SortedDictionary.Add O(log n) O(n log n) ImmutableSortedDictionary.Add O(log n)

ImmutableList 在 for 循环内的效果较差。 使用 foreach 循环枚举 ImmutableList 很有效,因为 ImmutableList 使用二进制树来存储其数据,而不是像 List 那样使用简单数组。 数组可以非常快速地编入索引,但必须向下遍历二进制树,直到找到具有所需索引的节点。
此外,SortedSet 与 ImmutableSortedSet 的复杂性相同。 这是因为它们都使用了二进制树。 当然,显著的差异在于 ImmutableSortedSet 使用不可变的二进制树。 由于 ImmutableSortedSet 还提供了一个允许变化的 System.Collections.Immutable.ImmutableSortedSet.Builder 类,因此可以同时实现不可变性和保障性能。

如何选择集合

顺序列表(检索元素值后就将该元素丢弃)

  • FIFO :Queue/ Queue/
  • LIFO :Stack 类或 Stack 泛型类
  • 多个线程进行安全访问,ConcurrentQueue/ConcurrentStack
  • 不可变性, ImmutableQueue 和 ImmutableStack
  • LinkedList 泛型类允许从开头到末尾或从末尾到开头的顺序访问

按索引访问每个元素

  • ArrayList 和 StringCollection 类以及 List 泛型类按从零开始的元素索引提供对其元素的访问。 如果要获得不可变性,请考虑不可变泛型版本 ImmutableArray 和 ImmutableList
  • Hashtable、SortedList、ListDictionary 和 StringDictionary 类以及 Dictionary<TKey,TValue> 和 SortedDictionary<TKey,TValue> 泛型类按元素的键提供对其元素的访问。 此外,还有几个相应类型的不可变版本:ImmutableHashSet、ImmutableDictionary<TKey,TValue>、ImmutableSortedSet 和 ImmutableSortedDictionary<TKey,TValue>。
  • NameObjectCollectionBase 和 NameValueCollection 类以及 KeyedCollection<TKey,TItem> 和 SortedList<TKey,TValue> 泛型类按从零开始的元素索引或元素的键提供对其元素的访问。

每个元素都包含一个值、一个键和一个值的组合或一个键和多个值的组合

  • 一个值:使用任何基于 IList 接口或 IList 泛型接口的集合。 要获得不可变选项,请考虑 IImmutableList 泛型接口。
  • 键值对:使用任何基于 IDictionary 接口或 IDictionary<TKey,TValue> 泛型接口的集合。 要获得不可变选项,请考虑 IImmutableSet 或 IImmutableDictionary<TKey,TValue> 泛型接口。
  • 带有嵌入键的值:使用 KeyedCollection<TKey,TItem> 泛型类。
  • 一个键和多个值:使用 NameValueCollection 类。

需要快速搜索和信息检索

  • 对于小集合(10 项或更少),ListDictionary 速度比 Hashtable 快。 Dictionary<TKey,TValue> 泛型类提供比 SortedDictionary<TKey,TValue> 泛型类更快的查找。 多线程的实现为 ConcurrentDictionary<TKey,TValue>。 ConcurrentBag 为无序数据提供快速的多线程插入。

只接受字符串的集合

  • StringCollection(基于 IList)和 StringDictionary(基于 IDictionary)位于 System.Collections.Specialized 命名空间。
  • 此外,通过指定其泛型类参数的 String 类,可以使用 System.Collections.Generic 命名空间中的任何泛型集合类作为强类型字符串集合。 例如,可以将变量声明为采用 List 或 Dictionary<String,String> 类型。

以与输入方式不同的方式对元素进行排序

  • Hashtable 类按其哈希代码对其元素进行排序。
  • SortedList 类以及 SortedList<TKey,TValue> 和 SortedDictionary<TKey,TValue> 泛型类按键对元素进行排序。 排序顺序的依据为,实现 SortedList 类的 IComparer 接口和实现 SortedList<TKey,TValue> 和SortedDictionary<TKey,TValue> 泛型类的 IComparer 泛型接口。 在这两种泛型类型中,虽然 SortedDictionary<TKey,TValue> 的性能优于 SortedList<TKey,TValue>,但 SortedList<TKey,TValue> 占用的内存更少。
  • ArrayList 提供了一种 Sort 方法,此方法采用 IComparer 实现作为参数。 其泛型对应项(List 泛型类)提供一种 Sort 方法,此方法采用 IComparer 泛型接口的实现作为参数。

泛型集合为何比非泛型好

  • 泛型时不必对元素进行装箱
  • 可获得类型安全的自动化优点而无需从基集合类型派生和实现特定类型的成员

集合内的比较和排序

检查相等

泛型:

  • 如果类型 T 实现 IEquatable 泛型接口,则相等比较器是该接口的 Equals 方法。
  • 如果类型 T 未实现 IEquatable,则使用 Object.Equals 。
    此外,字典集合的某些构造函数重载接受 IEqualityComparer 实现,用于比较键是否相等。

排序

- 如果类型 T 实现 System.IComparable<T> 泛型接口,则默认比较器是该接口的 IComparable<T>.CompareTo(T) 方法。
- 如果类型 T 实现非泛型 System.IComparable 接口,则默认比较器是该接口的 IComparable.CompareTo(Object) 方法。
- 如果类型 T 未实现任何接口,则没有默认比较器,必须显式提供一个比较器或比较委托。

为了提供显式比较,某些方法接受 IComparer 实现作为参数。 例如, List.Sort 方法接受 System.Collections.Generic.IComparer 实现。
参考https://docs.microsoft.com/zh-cn/dotnet/standard/collections/hashtable-and-dictionary-collection-types
总结:一般处理几百几千数据除特殊要求外。List都够用,具体可以参考我的多线程和代码优化部分

.NET 基础-3

上一篇:网络资源控制-Netfiter防火墙设置


下一篇:ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2