C# 2.0에서는 제네릭 기능이 도입되면서 C# 1.0에서 복잡한 코드를 작성해야 했던 일부 기능을 어느 정도 완성할 수 있게 되었습니다. 하지만 개발자로서 나는 Generics에 대해 애증의 관계를 가지고 있습니다. 제가 좋아하는 것은 강력한 기능과 이 기능으로 인한 효율성 향상이지만, 제가 싫어하는 것은 Generic이 복잡할 때 상당히 복잡한 구문 구조를 제시한다는 것입니다. . 이러한 복잡성은 초보자에게만 해당되는 것이 아니라 개발 경험이 있는 일부 .NET 개발자에게는 익히기 쉽지 않은 기능이기도 합니다.
다음으로 C# 2.0에 추가된 기능인 제네릭을 살펴보겠습니다.
1. 제네릭의 기본 기능 개요:
실제 프로젝트 개발에서 객체를 매개변수 유형 및 반환 유형으로 사용하는 모든 API에는 어느 시점에서 강력한 유형 변환이 포함될 수 있습니다. 강력한 유형 변환에 대해 많은 개발자의 첫 번째 반응은 "효율성"이라고 추정됩니다. 강력한 유형의 장점과 단점은 주로 사용자가 사용하는 환경에 따라 결정됩니다. 강한 타이핑에 관한 문제는 이 글의 초점이 아니므로 자세히 소개하지 않겠습니다.
제네릭은 "알고리즘 재사용"이라는 또 다른 형태의 코드 재사용을 지원하는 CLR 및 C#에서 제공하는 특수 메커니즘입니다. 제네릭은 유형 및 메소드의 매개변수화를 구현합니다. 제네릭 유형 및 메소드를 통해 매개변수는 사용자에게 사용할 유형을 알려줄 수도 있습니다.
제네릭이 가져오는 이점: 더 나은 컴파일 타임 검사, 코드에 직접 표현될 수 있는 더 많은 정보, 더 많은 IDE 지원 및 더 나은 성능. 어떤 사람들은 제네릭이 왜 그렇게 많은 이점을 가져오는지 궁금해할 수 있습니다. 서로 다른 유형을 구별할 수 없는 일반 API를 사용하는 것은 동적 환경에서 해당 API에 액세스하는 것과 같습니다.
CLR은 일반 참조 및 일반 값 유형의 생성을 허용하지만 일반 열거형의 생성을 허용하지 않습니다. CLR은 일반 인터페이스 및 일반 대리자의 생성을 허용합니다. 값 유형 또는 인터페이스를 정의합니다. 제네릭 형식이나 메서드를 정의할 때 해당 형식에 대해 지정된 모든 변수(예: T)를 형식 매개변수라고 합니다. (T는 변수 이름이고, T는 데이터 유형이 사용될 수 있는 소스 코드의 어느 곳에서나 사용될 수 있습니다.) C#에서 일반 매개변수 변수는 T가 되거나 최소한 대문자 T로 시작합니다.
2. 제네릭 클래스, 제네릭 인터페이스 및 제네릭 대리자 개요:
1. 제네릭 클래스:
제네릭 유형은 여전히 유형이므로 모든 유형에서 검색할 수 있습니다. 유도. 제네릭 형식을 사용하고 형식 인수를 지정하면 제네릭이 파생된 형식에서 파생된 CLR의 새 형식 개체를 정의하게 됩니다. 일반 유형 매개변수를 사용하는 한 가지 방법은 JIT 컴파일 중에 CLR이 IL을 획득하고 이를 지정된 유형 인수로 바꾼 다음 적절한 네이티브 코드를 생성하는지 확인하는 것입니다.
제네릭 유형 매개변수에 대해 유형 인수가 제공되지 않으면 제네릭 유형이 바인딩 해제됩니다. 유형 인수가 지정된 경우 유형은 생성된 유형입니다. 생성된 유형은 개방형이거나 폐쇄형일 수 있습니다. 개방형 유형에는 ixngcanshu 클래스도 포함되어 있지만 폐쇄형 유형은 개방형이 아니며 유형의 모든 부분이 명시적입니다. 모든 코드는 실제로 둘러싸는 생성 유형의 컨텍스트 내에서 실행됩니다.
.NET에서 일반 클래스의 적용은 주로 컬렉션 클래스에 있으며 대부분의 컬렉션 클래스는 System.Collections.Generic 및 System.Collections.ObjectModel 클래스에 있습니다. 다음은 일반 컬렉션 클래스에 대한 간략한 소개입니다.
(1).SynchronizedCollection: 일반 매개 변수로 지정된 유형의 개체를 요소로 포함하는 스레드로부터 안전한 컬렉션을 제공합니다.
[ComVisible(false)] public class SynchronizedCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable { /// <summary> /// 初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 类的新实例。 /// </summary> public SynchronizedCollection(); /// <summary> /// 通过用于对线程安全集合的访问进行同步的对象来初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 类的新实例。 /// </summary> /// <param name="syncRoot">用于对线程安全集合的访问进行同步的对象。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 为 null。</exception> public SynchronizedCollection(object syncRoot); /// <summary> /// 使用指定的可枚举元素列表和用于对线程安全集合的访问进行同步的对象来初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 类的新实例。 /// </summary> /// <param name="syncRoot">用于对线程安全集合的访问进行同步的对象。</param><param name="list">用于初始化线程安全集合的元素的 <see cref="T:System.Collections.Generic.IEnumerable`1"/> 集合。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 或 <paramref name="list"/> 为 null。</exception> public SynchronizedCollection(object syncRoot, IEnumerable<T> list); /// <summary> /// 使用指定的元素数组和用于对线程安全集合的访问进行同步的对象来初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 类的新实例。 /// </summary> /// <param name="syncRoot">用于对线程安全集合的访问进行同步的对象。</param><param name="list">用于初始化线程安全集合的 <paramref name="T"/> 类型元素的 <see cref="T:System.Array"/>。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 或 <paramref name="list"/> 为 null。</exception> public SynchronizedCollection(object syncRoot, params T[] list); /// <summary> /// 将项添加到线程安全只读集合中。 /// </summary> /// <param name="item">要添加到集合的元素。</param><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> public void Add(T item); /// <summary> /// 从集合中移除所有项。 /// </summary> public void Clear(); /// <summary> /// 从特定索引处开始,将集合中的元素复制到指定的数组。 /// </summary> /// <param name="array">从集合中复制的 <paramref name="T "/>类型元素的目标 <see cref="T:System.Array"/>。</param><param name="index">复制开始时所在的数组中的从零开始的索引。</param> public void CopyTo(T[] array, int index); /// <summary> /// 确定集合是否包含具有特定值的元素。 /// </summary> /// /// <returns> /// 如果在集合中找到元素值,则为 true;否则为 false。 /// </returns> /// <param name="item">要在集合中定位的对象。</param><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> public bool Contains(T item); /// <summary> /// 返回一个循环访问同步集合的枚举数。 /// </summary> /// /// <returns> /// 一个 <see cref="T:System.Collections.Generic.IEnumerator`1"/>,用于访问集合中存储的类型的对象。 /// </returns> public IEnumerator<T> GetEnumerator(); /// <summary> /// 返回某个值在集合中的第一个匹配项的索引。 /// </summary> /// /// <returns> /// 该值在集合中的第一个匹配项的从零开始的索引。 /// </returns> /// <param name="item">从集合中移除所有项。</param><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> public int IndexOf(T item); /// <summary> /// 将一项插入集合中的指定索引处。 /// </summary> /// <param name="index">要从集合中检索的元素的从零开始的索引。</param><param name="item">要作为元素插入到集合中的对象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> public void Insert(int index, T item); /// <summary> /// 从集合中移除指定项的第一个匹配项。 /// </summary> /// /// <returns> /// 如果从集合中成功移除了项,则为 true;否则为 false。 /// </returns> /// <param name="item">要从集合中移除的对象。</param> public bool Remove(T item); /// <summary> /// 从集合中移除指定索引处的项。 /// </summary> /// <param name="index">要从集合中检索的元素的从零开始的索引。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception> public void RemoveAt(int index); /// <summary> /// 从集合中移除所有项。 /// </summary> protected virtual void ClearItems(); /// <summary> /// 将一项插入集合中的指定索引处。 /// </summary> /// <param name="index">集合中从零开始的索引,在此处插入对象。</param><param name="item">要插入到集合中的对象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> protected virtual void InsertItem(int index, T item); /// <summary> /// 从集合中移除指定 <paramref name="index"/> 处的项。 /// </summary> /// <param name="index">要从集合中检索的元素的从零开始的索引。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception> protected virtual void RemoveItem(int index); /// <summary> /// 使用另一项替换指定索引处的项。 /// </summary> /// <param name="index">要替换的对象的从零开始的索引。</param><param name="item">要替换的对象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception> protected virtual void SetItem(int index, T item); /// <summary> /// 返回一个循环访问同步集合的枚举数。 /// </summary> /// /// <returns> /// 一个 <see cref="T:System.Collections.Generic.IEnumerator`1"/>,用于访问集合中存储的类型的对象。 /// </returns> IEnumerator IEnumerable.GetEnumerator(); /// <summary> /// 从特定索引处开始,将集合中的元素复制到指定的数组。 /// </summary> /// <param name="array">从集合中复制的 <paramref name="T"/> 类型元素的目标 <see cref="T:System.Array"/>。</param><param name="index">复制开始时所在的数组中的从零开始的索引。</param> void ICollection.CopyTo(Array array, int index); /// <summary> /// 向集合中添加一个元素。 /// </summary> /// /// <returns> /// 新元素的插入位置。 /// </returns> /// <param name="value">要添加到集合中的对象。</param> int IList.Add(object value); /// <summary> /// 确定集合是否包含具有特定值的元素。 /// </summary> /// /// <returns> /// 如果在集合中找到元素 <paramref name="value"/>,则为 true;否则为 false。 /// </returns> /// <param name="value">要在集合中定位的对象。</param><exception cref="T:System.ArgumentException"><paramref name="value"/> 不是集合所含类型的对象。</exception> bool IList.Contains(object value); /// <summary> /// 确定集合中某个元素的从零开始的索引。 /// </summary> /// /// <returns> /// 如果在集合中找到,则为 <paramref name="value"/> 的索引;否则为 -1。 /// </returns> /// <param name="value">集合中要确定其索引的元素。</param> int IList.IndexOf(object value); /// <summary> /// 将某个对象插入到集合中的指定索引处。 /// </summary> /// <param name="index">从零开始的索引,将在该位置插入 <paramref name="value"/>。</param><param name="value">要在集合中插入的对象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的项数。</exception><exception cref="T:System.ArgumentException">设置的 <paramref name="value"/> 为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception> void IList.Insert(int index, object value); /// <summary> /// 从集合中移除作为元素的指定对象的第一个匹配项。 /// </summary> /// <param name="value">要从集合中移除的对象。</param> void IList.Remove(object value); }
(2).KeyedByTypeCollection: 항목이 키로 사용되는 유형인 컬렉션을 제공합니다.
[__DynamicallyInvokable] public class KeyedByTypeCollection<TItem> : KeyedCollection<Type, TItem> { /// <summary> /// 初始化 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 类的新实例。 /// </summary> public KeyedByTypeCollection(); /// <summary> /// 根据指定的对象枚举初始化 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 类的新实例。 /// </summary> /// <param name="items">泛型类型 <see cref="T:System.Object"/> 的 <see cref="T:System.Collections.Generic.IEnumerable`1"/>,用于初始化集合。</param><exception cref="T:System.ArgumentNullException"><paramref name="items"/> 为 null。</exception> public KeyedByTypeCollection(IEnumerable<TItem> items); /// <summary> /// 返回集合中第一个具有指定类型的项。 /// </summary> /// /// <returns> /// 如果为引用类型,则返回类型 <paramref name="T"/> 的对象;如果为值类型,则返回类型 <paramref name="T"/> 的值。 如果集合中不包含类型 <paramref name="T"/> 的对象,则返回类型的默认值:如果是引用类型,默认值为 null;如果是值类型,默认值为 0。 /// </returns> /// <typeparam name="T">要在集合中查找的项的类型。</typeparam> [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public T Find<T>(); /// <summary> /// 从集合中移除具有指定类型的对象。 /// </summary> /// /// <returns> /// 从集合中移除的对象。 /// </returns> /// <typeparam name="T">要从集合中移除的项的类型。</typeparam> [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public T Remove<T>(); /// <summary> /// 返回 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 中包含的类型 <paramref name="T"/> 的对象的集合。 /// </summary> /// /// <returns> /// 一个类型 <paramref name="T"/> 的 <see cref="T:System.Collections.ObjectModel.Collection`1"/>,包含来自原始集合的类型 <paramref name="T"/> 的对象。 /// </returns> /// <typeparam name="T">要在集合中查找的项的类型。</typeparam> [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public Collection<T> FindAll<T>(); /// <summary> /// 从集合中移除所有具有指定类型的元素。 /// </summary> /// /// <returns> /// <see cref="T:System.Collections.ObjectModel.Collection`1"/>,包含来自原始集合的类型 <paramref name="T"/> 的对象。 /// </returns> /// <typeparam name="T">要从集合中移除的项的类型。</typeparam> [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public Collection<T> RemoveAll<T>(); /// <summary> /// 获取集合中包含的某个项的类型。 /// </summary> /// /// <returns> /// 集合中指定的 <paramref name="item"/> 的类型。 /// </returns> /// <param name="item">集合中要检索其类型的项。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 为 null。</exception> [__DynamicallyInvokable] protected override Type GetKeyForItem(TItem item); /// <summary> /// 在集合中的特定位置插入一个元素。 /// </summary> /// <param name="index">从零开始的索引,应在该位置插入 <paramref name="item"/>。</param><param name="item">要在集合中插入的对象。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 为 null。</exception> [__DynamicallyInvokable] protected override void InsertItem(int index, TItem item); /// <summary> /// 使用一个新对象替换指定索引处的项。 /// </summary> /// <param name="index">要替换的 <paramref name="item"/> 的从零开始的索引。</param><param name="item">要添加到集合中的对象。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 为 null。</exception> [__DynamicallyInvokable] protected override void SetItem(int index, TItem item); }
2. 제네릭 인터페이스 및 제네릭 대리자:
제네릭의 주요 기능은 제네릭 참조 유형과 참조 유형을 정의하는 것입니다. 참조 형식 또는 값 형식은 형식 인수를 지정하여 일반 인터페이스를 구현할 수도 있고 형식 인수를 지정하지 않은 상태로 두어 일반 인터페이스를 구현할 수도 있습니다.
일반 인터페이스 IEnumerable을 자세히 살펴보겠습니다. IEnumerable은 제네릭이 아닌 컬렉션에 대한 간단한 반복을 지원하는 열거자를 노출합니다.
[ComVisible(true)] [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] [__DynamicallyInvokable] public interface IEnumerable { /// <summary> /// 返回一个循环访问集合的枚举数。 /// </summary> /// /// <returns> /// 一个可用于循环访问集合的 <see cref="T:System.Collections.IEnumerator"/> 对象。 /// </returns> /// <filterpriority>2</filterpriority> [DispId(-4)] [__DynamicallyInvokable] IEnumerator GetEnumerator(); }
CLR支持泛型委托,目的是保证任何类型的对象都能以一种类型安全的方式传给一个回调方法。泛型委托允许一个孩子类型实例在传给一个回调方法时不执行任何装箱处理。委托时机只提供了4个方法:一个构造器,一个Invlke方法,一个BeginInvoke方法和一个EndInvoke方法。如果定义的一个委托类型指定了类型参数,编译器会定义委托类的方法,用指定的类型参数替换方法的参数类型和值类型。
以上是对泛型类、泛型接口和泛型委托的简单了解,本文的目的主要是讲解泛型方法,下面我们具体了解一些泛型泛型的知识。
三.泛型方法解析:
1.泛型方法概述:
定义泛型类、结构或接口时,类型中定义的任何方法都可引用类型指定的一个类型参数。类型参数可以作为方法的参数,作为方法的返回值,或者作为方法内部定义的一个局部变量来使用。CLR允许一个方法指定它独有的类型参数,这些类型参数可用于参数、返回值、或者局部变量。
C#编译器支持在调用一个泛型方法时进行类型推断。执行类型推断时,C#使用变量的数据类型,而不是由变量引用的对象的实际类型。一个类型可以定义多个方法,让其中一个方法接受具体的数据类型,让另一个方法接受泛型类型参数。
泛型方法示例:
List<TOutput> ConverAll<TOutput>(Conver<T,TOutput> conv) List<TOutput>:返回类型(一个泛型列表)。 ConverAll:方法名。 <TOutput>:类型参数。 Conver<T,TOutput>:参数类型(泛型委托)。 conv:参数名。
对以上的示例代码分析,需要掌握:为每个类型参数使用一个不同的类型,在整体应用这些类型参数。
(1).首先替换包含方法(List8742468051c85b06f0a0af9e3e506b5c的T部分)的那个类型的类型参数,如将T替换为string:
Liste7370627424990418781085abf28678e ConverAlle7370627424990418781085abf28678e(Conver48a59f7eb6ffe302e19233ffce7d48e3 conv)
(2).处理完T后,再需要处理的就是TOutput,可以看出它是一个方法类型参数,这里采用guid替换TOutput。
Listbf63081c96db1d9c00822a55a6b1f895 ConverAll(Conver39395d6745762b89ff8067aea81a6998 conv)
对TOutput赋予类型实参后,可以移除生命中的类型参数e7370627424990418781085abf28678e,将方法堪称非泛型方法,如上。以上的示例可以处理一个字符串列表,用一个转换器来生成一个Guid列表。
将原始列表中的每个元素都转换成目标类型,将转换后的元素添加到一个列表中,最后返回这个列表。以上的处理方式,主要将其泛型方法的参数进行逐一的细化,无论在什么学科,都需要将复杂的问题进行简单化,将抽象的问题具体化,这也是一种常用的处理方式。
2.类型约束:
约束的作用是限制能指定成泛型实参的类型数量。通过限制类型的数量,我们可以对那些类型执行更多的操作。约束可以应用于一个泛型类型的类型参数,也可以应用于一个泛型方法的类型参数。CLR不允许基于类型参数名称或约束进行重载,只能基于元数对类型或方法进行重载。不允许为重写方法的类型参数指定任何约束,但是类型实参的名称是可以改变的。
泛型约束的操作,约束要放到泛型方法或泛型类型声明的末尾,并由上下文关键where引入。
(1).引用类型约束:
引用类型约束:用于确保使用的类型实参是引用类型。(表示为:T:class,且必须为类型参数指定的第一个约束。)
(2).值类型约束:
值类型约束:用于确保使用的类型参数是指类型。(表示为:T:struct,可空类型不包含在内)
(3).构造函数类型约束:
构造函授类型约束:指定所有类型参数的最后一个约束,它检查类型实参是否有一个可用于创建实例的无参构造函数。(表示为:T:new())适用于所有值类型,所有没有显示声明构造函数的非静态、非抽象类,所有显示声明了一个公共无参构造函数的非抽象类。
(4).转换类型约束:
转换类型约束:允许你指定另一个类型,类型实参必须可以通过一致性、引用或装箱转换隐式地转换为该类型。还可以规定类型实参必须可以转换为另一个类型实参。(例:class Sample8742468051c85b06f0a0af9e3e506b5c where T:Stream)
(5).组合约束:
组合约束:所个约束组合在一起的约束,但是组合约束也有限制条件。因为没有任何类型即是引用类型,又是值类型。由于每一个值都有一个无参构造函数,所以假如已经有一个值类型约束,就不允许再指定一个构造函数约束。如果存在多个类型约束,并且其中一个为类,那么它应该出现在接口的前面,而且我们不能多次指定同一个接口。不同的类型参数可以用不同的约束,分别由一个where引入。
备注:类型推断只适用于泛型方法,不适用于泛型类型。
以上是对泛型方法的相关概念和约束做了简单的解析,接下来看一下.NET中一些发行方法的具体实现:
/// <summary> /// 封装一个方法,该方法具有四个参数并且不返回值。 /// </summary> /// <param name="arg1">此委托封装的方法的第一个参数。</param><param name="arg2">此委托封装的方法的第二个参数。</param><param name="arg3">此委托封装的方法的第三个参数。</param><param name="arg4">此委托封装的方法的第四个参数。</param><typeparam name="T1">此委托封装的方法的第一个参数类型。</typeparam><typeparam name="T2">此委托封装的方法的第二个参数类型。</typeparam><typeparam name="T3">此委托封装的方法的第三个参数类型。</typeparam><typeparam name="T4">此委托封装的方法的第四个参数类型。</typeparam><filterpriority>2</filterpriority> [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")] [__DynamicallyInvokable] public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
/// <summary> /// 表示比较同一类型的两个对象的方法。 /// </summary> /// /// <returns> /// 一个有符号整数,指示 <paramref name="x"/> 与 <paramref name="y"/> 的相对值,如下表所示。 值 含义 小于 0 <paramref name="x"/> 小于 <paramref name="y"/>。 0 <paramref name="x"/> 等于 <paramref name="y"/>。 大于 0 <paramref name="x"/> 大于 <paramref name="y"/>。 /// </returns> /// <param name="x">要比较的第一个对象。</param><param name="y">要比较的第二个对象。</param><typeparam name="T">要比较的对象的类型。</typeparam><filterpriority>1</filterpriority> [__DynamicallyInvokable] public delegate int Comparison<in T>(T x, T y);
四.泛型方法应用代码示例:
以上讲解的有关泛型方法的内容,这里提供一个有关泛型方法操作XML的代码:
/// <summary> /// 泛型方法:编译器能够根据传入的方法参数推断类型参数;它无法仅从约束或返回值推断类型参数 /// </summary> public class ObjectXmlSerializer { /// <summary> /// 文件的反序列化 /// </summary> /// <typeparam name="T">返回值类型</typeparam> /// <param name="fileName"></param> /// <returns> /// 如果日志启用,则发生异常时,异常写入日志,若日志没有开启,则直接抛出异常信息 /// loggingEnabled==true: Null is returned if any error occurs. /// loggingEnabled==false: throw exception /// </returns> public static T LoadFromXml<T>(string fileName) where T : class { return LoadFromXml<T>(fileName, true); } /// <summary> /// 文件反序列化,若发生异常,异常信息写入日志 /// </summary> /// <typeparam name="T">加载类的类型</typeparam> /// <param name="fileName">文件名字</param> /// <param name="loggingEnabled">启用日志记录</param> /// <returns></returns> public static T LoadFromXml<T>(string fileName, bool loggingEnabled) where T : class { FileStream fs = null; try { var serializer = new XmlSerializer(typeof(T)); fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); //反序列化对象 return (T)serializer.Deserialize(fs); } catch (Exception e) { if (loggingEnabled) { //文件异常,写入日志 LogLoadFileException(fileName, e); return null; } else { throw new Exception(e.Message); } } finally { if (fs != null) fs.Close(); } } /// <summary> /// 序列化一个对象到文件中. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="fileName">文件名</param> /// <param name="data">待序列化的数据</param> /// <returns> /// 如果日志启用,则发生异常时,异常写入日志,若日志没有开启,则直接抛出异常信息 /// loggingEnabled==true: log exception /// loggingEnabled==false: throw exception /// </returns> public static void SaveToXml<T>(string fileName, T data) where T : class { SaveToXml(fileName, data, true); } /// <summary> /// 文件反序列化,若发生异常,异常信息写入日志 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="fileName">文件名</param> /// <param name="data">发序列化对象</param> /// <param name="loggingEnabled">是否启用日志</param> public static void SaveToXml<T>(string fileName, T data, bool loggingEnabled) where T : class { FileStream fs = null; try { var serializer = new XmlSerializer(typeof(T)); fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); //序列化对象 serializer.Serialize(fs, data); } catch (Exception e) { if (loggingEnabled) LogSaveFileException(fileName, e); else { throw new Exception(e.Message); } } finally { if (fs != null) fs.Close(); } } /// <summary> /// 序列化 /// XML & Datacontract Serialize & Deserialize Helper /// </summary> /// <typeparam name="T">T指定必须为class类型</typeparam> /// <param name="serialObject"></param> /// <returns></returns> public static string XmlSerializer<T>(T serialObject) where T : class { var ser = new XmlSerializer(typeof(T)); //MemoryStream实现对内存的读写,而不是对持久性存储器进行读写 //MemoryStream封装以无符号字节数组形式存储的数据,该数组在创建MemoryStream对象时被初始化, //或者该数组可创建为空数组。可在内存中直接访问这些封装的数据。 //内存流可降低应用程序中对临时缓冲区和临时文件的需要。 var mem = new MemoryStream(); var writer = new XmlTextWriter(mem, UTF8); ser.Serialize(writer, serialObject); writer.Close(); return UTF8.GetString(mem.ToArray()); } /// <summary> /// 反序列化 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="str"></param> /// <returns></returns> public static T XmlDeserialize<T>(string str) where T : class { var mySerializer = new XmlSerializer(typeof(T)); var mem2 = new StreamReader(new MemoryStream(UTF8.GetBytes(str)), UTF8); return (T)mySerializer.Deserialize(mem2); } /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="xmlData"></param> /// <returns>返回值类型为传入的类型</returns> public static T DataContractDeserializer<T>(string xmlData) where T : class { var stream = new MemoryStream(UTF8.GetBytes(xmlData)); var reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas()); var ser = new DataContractSerializer(typeof(T)); var deserializedPerson = (T)ser.ReadObject(reader, true); reader.Close(); stream.Close(); return deserializedPerson; } /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="myObject"></param> /// <returns></returns> public static string DataContractSerializer<T>(T myObject) where T : class { var stream = new MemoryStream(); var ser = new DataContractSerializer(typeof(T)); ser.WriteObject(stream, myObject); stream.Close(); return UTF8.GetString(stream.ToArray()); } /// <summary> /// 序列化时异常日志 /// </summary> /// <param name="fileName">文件名</param> /// <param name="ex">异常</param> [Conditional("TRACE")] private static void LogLoadFileException(string fileName, Exception ex) { var sb = new StringBuilder(); sb.Append("Fail to load xml file: "); sb.Append(fileName + Environment.NewLine); sb.Append(ex); //写入日志记录中方法 // Logger.LogEvent(LogCategory, LogEventLoadFileException, sb.ToString()); } /// <summary> /// 反序列化时异常日志 /// </summary> /// <param name="fileName">文件名</param> /// <param name="ex">异常</param> [Conditional("TRACE")] private static void LogSaveFileException(string fileName, Exception ex) { var sb = new StringBuilder(); sb.Append("Fail to save xml file: "); sb.Append(fileName + Environment.NewLine); sb.Append(ex); } /// <summary> /// 将xml字符串序列化为数据流(数据流编码为ASCII,UTF8) /// </summary> /// <returns>字符串转换到流</returns> public static MemoryStream StringXmlToStream(string strXml,Encoding encod) { MemoryStream memoryStream = null; try { Encoding encoding; if (Equals(encod, ASCII)) { encoding = new ASCIIEncoding(); } else { encoding = new UTF8Encoding(); } var byteArray = encoding.GetBytes(strXml); memoryStream = new MemoryStream(byteArray); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; } catch (IOException ex) { throw new IOException(ex.Message); } finally { if (memoryStream != null) memoryStream.Close(); } } }
以上的代码就不做赘述,需要次代码的可以使用。
五.总结:
本文讲解了C#2.0引入的泛型知识,主要包含泛型类、泛型接口、泛型委托,并且重点讲解了泛型方法,已经泛型的约束分类。最后给了一些利用泛型方法操作xml的方法。希望以上的讲解可以帮助到正在想学习的人。
爱知求真,静心钻研,虚心学习,务实创新,细致平和。