Heim >Backend-Entwicklung >C#.Net-Tutorial >Analyse generischer C#-Methoden
Mit der Einführung von Generika wurde in C# 2.0 die Funktionalität von C# erheblich verbessert. Es kann einige Funktionen vervollständigen, die das Schreiben komplexer Codes in C# 1.0 erforderten. Aber als Entwickler habe ich eine Hassliebe zu Generika. Was mir gefällt, sind die leistungsstarken Funktionen und die Effizienzsteigerungen, die diese Funktion mit sich bringt. Was ich jedoch hasse, ist, dass Generika, wenn sie komplex sind, recht komplizierte Syntaxstrukturen aufweisen. . Diese Komplexität ist nicht nur etwas für Anfänger, sondern auch eine Funktion, die für einige .NET-Entwickler mit Entwicklungserfahrung nicht einfach zu beherrschen ist.
Als Nächstes werfen wir einen Blick auf die in C# 2.0 hinzugefügten Funktionen: Generics.
1. Überblick über die Grundfunktionen von Generika:
In der tatsächlichen Projektentwicklung kann jede API, die ein Objekt als Parametertyp und Rückgabetyp verwendet, irgendwann eine starke Typkonvertierung erfordern. Wenn es um die Konvertierung starker Typen geht, wird geschätzt, dass die erste Reaktion vieler Entwickler „Effizienz“ ist. Die Vor- und Nachteile der starken Typisierung hängen hauptsächlich von der Umgebung ab, in der der Benutzer sie verwendet Was das starke Tippen angeht, ist das Thema nicht der Schwerpunkt dieses Artikels und ich werde es nicht im Detail vorstellen.
Generics sind ein spezieller Mechanismus, der von CLR und C# bereitgestellt wird und eine andere Form der Code-Wiederverwendung unterstützt, nämlich die „Algorithmus-Wiederverwendung“. Generische Typen und Methoden implementieren die Parametrisierung von Typen und Methoden. Außerdem können Parameter den Benutzern mitteilen, welcher Typ verwendet werden soll.
Vorteile von Generika: bessere Überprüfung zur Kompilierungszeit, mehr Informationen, die direkt im Code ausgedrückt werden können, mehr IDE-Unterstützung und bessere Leistung. Manche Leute fragen sich vielleicht, warum Generika so viele Vorteile bringen. Die Verwendung einer regulären API, die nicht zwischen verschiedenen Typen unterscheiden kann, ist gleichbedeutend mit dem Zugriff auf diese API in einer dynamischen Umgebung.
Die CLR ermöglicht die Erstellung von generischen Referenzen und generischen Werttypen, jedoch nicht die Erstellung von generischen Aufzählungen. Die CLR ermöglicht die Erstellung von generischen Referenzen. Werttypen oder Schnittstellen definieren. Beim Definieren eines generischen Typs oder einer generischen Methode werden alle für den Typ angegebenen Variablen (z. B. T) als Typparameter bezeichnet. (T ist ein Variablenname und T kann überall im Quellcode verwendet werden, wo ein Datentyp verwendet werden kann.) In C# werden generische Parametervariablen entweder zu T oder beginnen zumindest mit einem großen T.
2. Übersicht über generische Klassen, generische Schnittstellen und generische Delegaten:
1. Generische Klassen:
Generische Typen sind immer noch Typen und können daher von jedem Typ abgerufen werden Ableitung. Wenn Sie einen generischen Typ verwenden und Typargumente angeben, definieren Sie in der CLR ein neues Typobjekt, das von dem Typ abgeleitet ist, von dem der generische Typ abgeleitet ist. Eine Möglichkeit, generische Typparameter zu verwenden, besteht darin, sicherzustellen, dass die CLR während der JIT-Kompilierung die IL erhält, sie durch das angegebene Typargument ersetzt und dann den entsprechenden nativen Code erstellt.
Wenn für einen generischen Typparameter keine Typargumente bereitgestellt werden, ist der generische Typ ungebunden. Wenn ein Typargument angegeben wird, ist der Typ ein konstruierter Typ. Konstruierte Typen können offen oder geschlossen sein. Offene Typen enthalten auch eine Klasse, während geschlossene Typen nicht offen sind und jeder Teil des Typs explizit ist. Der gesamte Code wird tatsächlich im Kontext eines umschließenden konstruierten Typs ausgeführt.
Die Anwendung generischer Klassen in .NET erfolgt hauptsächlich in Sammlungsklassen, und die meisten Sammlungsklassen befinden sich in den Klassen System.Collections.Generic und System.Collections.ObjectModel. Im Folgenden finden Sie eine kurze Einführung in eine generische Sammlungsklasse:
(1).SynchronizedCollection: Stellt eine threadsichere Sammlung bereit, die Objekte des durch die generischen Parameter angegebenen Typs als Elemente enthält.
[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: Stellt eine Sammlung bereit, deren Elemente die als Schlüssel verwendeten Typen sind.
[__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. Generische Schnittstellen und generische Delegaten:
Die Hauptfunktion von Generics besteht darin, generische Referenztypen und Referenztypen zu definieren. Ein Referenztyp oder Werttyp kann eine generische Schnittstelle implementieren, indem er Typargumente angibt, oder er kann eine generische Schnittstelle implementieren, indem er die Typargumente nicht spezifiziert.
Schauen wir uns die generische Schnittstelle IEnumerable genauer an: Sie stellt einen Enumerator bereit, der eine einfache Iteration über eine nicht generische Sammlung unterstützt.
[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的方法。希望以上的讲解可以帮助到正在想学习的人。
爱知求真,静心钻研,虚心学习,务实创新,细致平和。