C#泛型方法解析
C#2.0引入了泛型这个特性,由于泛型的引入,在一定程度上极大的增强了C#的生命力,可以完成C#1.0时需要编写复杂代码才可以完成的一些功能。但是作为开发者,对于泛型可谓是又爱又恨,爱的是其强大的功能,以及该特性带来的效率的提升,恨的是泛型在复杂的时候,会呈现相当复杂的语法结构。这种复杂不仅是对于初学者,对于一些有开发经验的.NET开发者,也是一个不那么容易掌握的特性。
接下来我们来了解一下C#2.0加入的特性:泛型。
一.泛型的基本特性概述:
在实际项目开发中,任何API只要将object作为参数类型和返回类型使用,就可能在某个时候涉及强类型转换。提到强类型转换,估计很多开发者第一反应就是“效率”这个次,对于强类型的利弊主要看使用者使用的环境,天底下没有绝对的坏事,也没有绝对的好事,有关强类型的问题不是本次的重点,不做重点介绍。
泛型是CLR和C#提供的一种特殊机制,支持另一种形式的代码重用,即“算法重用”。泛型实现了类型和方法的参数化,泛型类型和方法也可以让参数告诉使用者使用什么类型。
泛型所带来的好处:更好的编译时检查,更多在代码中能直接表现的信息,更多的IDE支持,更好的性能。可能有人会疑问,为什么泛型会带来这么多好处,使用一个不能区分不同类型的常规API,相当于在一个动态环境中访问那个API。
CLR允许创建泛型引用和泛型值类型,但是不允许创建泛型枚举,并且CLR允许创建泛型接口和泛型委托,CLR允许在引用类型、值类型或接口中定义泛型方法。定义泛型类型或方法时,为类型指定了任何变量(如:T)都称为类型参数。(T是一个变量名,在源代码中能够使用一个数据类型的任何位置,都可以使用T)在C#中泛型参数变量要么成为T,要么至少一大写T开头。
二.泛型类、泛型接口和泛型委托概述:
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:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
[__DynamicallyInvokable]
public interface IEnumerable
{
/// <summary>
/// 返回一个循环访问集合的枚举数。
/// </summary>
///
/// <returns>
/// 一个可用于循环访问集合的 <see cref="T:System.Collections.IEnumerator"/> 对象。
/// </returns>
/// <filterpriority></filterpriority>
[DispId(-)]
[__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).首先替换包含方法(List<T>的T部分)的那个类型的类型参数,如将T替换为string:
List<TOutput> ConverAll<TOutput>(Conver<string,TOutput> conv)
(2).处理完T后,再需要处理的就是TOutput,可以看出它是一个方法类型参数,这里采用guid替换TOutput。
List<Guid> ConverAll(Conver<string,Guid> conv)
对TOutput赋予类型实参后,可以移除生命中的类型参数<TOutput>,将方法堪称非泛型方法,如上。以上的示例可以处理一个字符串列表,用一个转换器来生成一个Guid列表。
将原始列表中的每个元素都转换成目标类型,将转换后的元素添加到一个列表中,最后返回这个列表。以上的处理方式,主要将其泛型方法的参数进行逐一的细化,无论在什么学科,都需要将复杂的问题进行简单化,将抽象的问题具体化,这也是一种常用的处理方式。
2.类型约束:
约束的作用是限制能指定成泛型实参的类型数量。通过限制类型的数量,我们可以对那些类型执行更多的操作。约束可以应用于一个泛型类型的类型参数,也可以应用于一个泛型方法的类型参数。CLR不允许基于类型参数名称或约束进行重载,只能基于元数对类型或方法进行重载。不允许为重写方法的类型参数指定任何约束,但是类型实参的名称是可以改变的。
泛型约束的操作,约束要放到泛型方法或泛型类型声明的末尾,并由上下文关键where引入。
(1).引用类型约束:
引用类型约束:用于确保使用的类型实参是引用类型。(表示为:T:class,且必须为类型参数指定的第一个约束。)
(2).值类型约束:
值类型约束:用于确保使用的类型参数是指类型。(表示为:T:struct,可空类型不包含在内)
(3).构造函数类型约束:
构造函授类型约束:指定所有类型参数的最后一个约束,它检查类型实参是否有一个可用于创建实例的无参构造函数。(表示为:T:new())适用于所有值类型,所有没有显示声明构造函数的非静态、非抽象类,所有显示声明了一个公共无参构造函数的非抽象类。
(4).转换类型约束:
转换类型约束:允许你指定另一个类型,类型实参必须可以通过一致性、引用或装箱转换隐式地转换为该类型。还可以规定类型实参必须可以转换为另一个类型实参。(例:class Sample<T> 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></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></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(, SeekOrigin.Begin);
return memoryStream;
}
catch (IOException ex)
{
throw new IOException(ex.Message);
}
finally
{
if (memoryStream != null) memoryStream.Close();
} } }
以上的代码就不做赘述,需要次代码的可以使用。
五.总结:
本文讲解了C#2.0引入的泛型知识,主要包含泛型类、泛型接口、泛型委托,并且重点讲解了泛型方法,已经泛型的约束分类。最后给了一些利用泛型方法操作xml的方法。希望以上的讲解可以帮助到正在想学习的人。
C#泛型方法解析的更多相关文章
- 干货!表达式树解析"框架"(3)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了 关于表达 ...
- ABP使用及框架解析系列 - [Unit of Work part.2-框架实现]
前言 ABP ABP是“ASP.NET Boilerplate Project”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开 ...
- IEnumerable<IEnumerable<string>>结构解析通用解决方案(支持指定属性顺序)
一.前言 类似如下字符串 "ID", "NameValue", "CodeValue", "ExchangeTypeValue&q ...
- String,StringBuffer和StringBuilder源码解析[基于JDK6]
最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...
- fastjson生成和解析json数据
本文讲解2点: 1. fastjson生成和解析json数据 (举例:4种常用类型:JavaBean,List<JavaBean>,List<String>,List<M ...
- Java泛型解析(03):虚拟机运行泛型代码
Java泛型解析(03):虚拟机运行泛型代码 Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...
- java基础(9) - 泛型解析
泛型 定义简单的泛型类 泛型方法 /** * 1.定义一个泛型类 * 在类名后添加类的泛型参数 <T> * 泛型类里面的所有T会根据创建泛型类时传入的参数确定类型 * 2.定义泛型方法 * ...
- ReactiveSwift源码解析(九) SignalProducerProtocol延展中的Start、Lift系列方法的代码实现
上篇博客我们聊完SignalProducer结构体的基本实现后,我们接下来就聊一下SignalProducerProtocol延展中的start和lift系列方法.SignalProducer结构体的 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
随机推荐
- Java MyBatis 插入数据库返回主键
最近在搞一个电商系统中由于业务需求,需要在插入一条产品信息后返回产品Id,刚开始遇到一些坑,这里做下笔记,以防今后忘记. 类似下面这段代码一样获取插入后的主键 User user = new User ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制
将通用的序列号生成器库 从SQL Server迁移到Mysql 遇到的一个问题,就是TimeStamp/RowVersion并发控制类型在非Microsoft SQL Server数据库中的实现.SQ ...
- C# i=0;i=i++,i的值是多少?
昨天看群里dalao们聊天,有一个人出来问这个问题 这个题应该是挺常见的 int i = 0, t; for(t = 0;t <= 5;t++) { ...
- SQL必备知识点
经典SQL语句大全 基础 1.说明:创建数据库.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备份数据的 device.说明:创建新表crea ...
- .NET Core的日志[1]:采用统一的模式记录日志
记录各种级别的日志是所有应用不可或缺的功能.关于日志记录的实现,我们有太多第三方框架可供选择,比如Log4Net.NLog.Loggr和Serilog 等,当然我们还可以选择微软原生的诊断框架(相关A ...
- 利用Node.js的Net模块实现一个命令行多人聊天室
1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...
- Xamarin与Visual stuido2015离线安装包分享
最近看见大伙留言才知道国内安装Xamarin开发原来这么艰辛啊! 第一:网速不快 第二:Android SDK下载受限 等等... 鉴于这些原因,特写下这篇文章以及分享打包好的离线包以帮助大家尽快体验 ...
- 在开源中国(oschina)git中新建标签(tags)
我今天提交代码到主干上面,本来想打个标签(tags)的. 因为我以前新建过标签(tags),但是我现在新建的时候不知道入库在哪了.怎么找也找不到了. 从网上找资料也没有,找客服没有人理我,看到一个交流 ...
- Spring6:基于注解的Spring MVC(上篇)
什么是Spring MVC Spring MVC框架是一个MVC框架,通过实现Model-View-Controller模式来很好地将数据.业务与展现进行分离.从这样一个角度来说,Spring MVC ...