Analysis of generic method of C

  • 2021-11-14 06:49:22
  • OfStack

C # 2.0 introduces generics, which greatly enhances the vitality of C # to a certain extent, and can complete some functions that C # 1.0 needs to write complex code to complete. However, as developers, they love and hate generics. What they love is their powerful functions and the efficiency improvement brought by this feature. What they hate is that generics will present quite complex grammatical structures when they are complex. This complexity is not only for beginners, but also for some experienced NET developers, and it is also a feature that is not so easy to master.

Next, let's take a look at the feature added by C # 2.0 under 1: generics.

1. Overview of basic features of generics:

In real-world project development, any API that uses object as a parameter type and a return type might at some point involve strong typing. When it comes to strong typing conversion, it is estimated that the first reaction of many developers is "efficiency". The advantages and disadvantages of strong typing mainly depend on the environment used by users. There is no absolute bad thing or absolute good thing in the world. The problem about strong typing is not the focus of this time, so we will not focus on it.

Generics are a special mechanism provided by CLR and C # that supports another form of code reuse, called "algorithm reuse." Generics implement parameterization of types and methods, and generic types and methods also allow parameters to tell consumers what type to use.

Benefits of generics: Better compile-time checking, more information that can be expressed directly in code, more IDE support, and better performance. One may wonder why generics bring so many benefits, and using a regular API that can't distinguish different types is equivalent to accessing that API in a dynamic environment.

CLR allows the creation of generic references and generic value types, but not generic enumerations, and CLR allows the creation of generic interfaces and generic delegates, and CLR allows the definition of generic methods in reference types, value types, or interfaces. When defining a generic type or method, any variable specified for the type (such as T) is called a type parameter. (T is a variable name, and T can be used wherever a data type can be used in the source code.) In C #, the generic parameter variable either becomes T or begins with at least 1 uppercase T.

2. Overview of Generic Classes, Generic Interfaces, and Generic Delegates:

1. Generic classes:

Generic types are still types, so they can be derived from any type. When you use a generic type and specify type arguments, you are actually defining a new type object in CLR, which is derived from the type from which the generic type is derived. When compiling JIT with a generic type parameter, CLR gets IL, replaces it with the specified type argument, and creates the appropriate native code.

If a type argument is not provided for a generic type parameter, it is an unbound generic type. If a type argument is specified, the type is a constructed type. Constructed types can be developed or enclosed. The developed type also contains a class ixngcanshu, while the enclosed type is not developed, and every part of the type is explicit. All code is actually executed in the context of a closed constructed type.

Generic classes are used in. NET mainly in collection classes, most of which are in System. Collections. Generic and System. Collections. ObjectModel classes. Here is a brief introduction to one generic collection class:

(1). SynchronizedCollection: Provides a thread-safe collection that contains as elements objects of the type specified by the generic parameter.


 [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>
 /// 返回1个循环访问同步集合的枚举数。
 /// </summary>
 /// 
 /// <returns>
 /// 1个 <see cref="T:System.Collections.Generic.IEnumerator`1"/>,用于访问集合中存储的类型的对象。
 /// </returns>
 public IEnumerator<T> GetEnumerator();
 /// <summary>
 /// 返回某个值在集合中的第1个匹配项的索引。
 /// </summary>
 /// 
 /// <returns>
 /// 该值在集合中的第1个匹配项的从零开始的索引。
 /// </returns>
 /// <param name="item">从集合中移除所有项。</param><exception cref="T:System.ArgumentException">设置的值为 null,或者不是集合的正确泛型类型 <paramref name="T"/>。</exception>
 public int IndexOf(T item);
 /// <summary>
 /// 将1项插入集合中的指定索引处。
 /// </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>
 /// 从集合中移除指定项的第1个匹配项。
 /// </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>
 /// 将1项插入集合中的指定索引处。
 /// </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>
 /// 使用另1项替换指定索引处的项。
 /// </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>
 /// 返回1个循环访问同步集合的枚举数。
 /// </summary>
 /// 
 /// <returns>
 /// 1个 <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>
 /// 向集合中添加1个元素。
 /// </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>
 /// 从集合中移除作为元素的指定对象的第1个匹配项。
 /// </summary>
 /// <param name="value">要从集合中移除的对象。</param>
 void IList.Remove(object value);
 }

(2). KeyedByTypeCollection: Provides a collection whose items are types used as keys.


 [__DynamicallyInvokable]
 public class KeyedByTypeCollection<TItem> : KeyedCollection<Type, TItem>
 {
 /// <summary>
 ///  Initialization  <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/>  Gets or sets a new instance of the. 
 /// </summary>
 public KeyedByTypeCollection();
 /// <summary>
 ///  Class based on the specified object enumeration  <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/>  Gets or sets a new instance of the. 
 /// </summary>
 /// <param name="items"> Generic type  <see cref="T:System.Object"/>  Adj.  <see cref="T:System.Collections.Generic.IEnumerable`1"/> That is used to initialize the collection. </param><exception cref="T:System.ArgumentNullException"><paramref name="items"/>  For  null . </exception>
 public KeyedByTypeCollection(IEnumerable<TItem> items);
 /// <summary>
 ///  Returns the first in the collection 1 Items of the specified type. 
 /// </summary>
 /// 
 /// <returns>
 ///  If it is a reference type, return type  <paramref name="T"/>  Object of; If it is a value type, return type  <paramref name="T"/>  The value of.   If the collection does not contain a type  <paramref name="T"/>  Returns the default value of the type: If it is a reference type, the default value is  null ; If it is a value type, the default value is  0 . 
 /// </returns>
 /// <typeparam name="T"> The type of item to find in the collection. </typeparam>
 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 public T Find<T>();
 /// <summary>
 ///  Removes an object of the specified type from the collection. 
 /// </summary>
 /// 
 /// <returns>
 ///  Objects removed from the collection. 
 /// </returns>
 /// <typeparam name="T"> The type of item to remove from the collection. </typeparam>
 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 public T Remove<T>();
 /// <summary>
 ///  Return  <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/>  Types contained in  <paramref name="T"/>  Gets or sets a collection of objects of. 
 /// </summary>
 /// 
 /// <returns>
 /// 1 Type  <paramref name="T"/>  Adj.  <see cref="T:System.Collections.ObjectModel.Collection`1"/> Contains the type from the original collection  <paramref name="T"/>  Object of. 
 /// </returns>
 /// <typeparam name="T"> The type of item to find in the collection. </typeparam>
 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 public Collection<T> FindAll<T>();
 /// <summary>
 ///  Removes all elements of the specified type from the collection. 
 /// </summary>
 /// 
 /// <returns>
 /// <see cref="T:System.Collections.ObjectModel.Collection`1"/> Contains the type from the original collection  <paramref name="T"/>  Object of. 
 /// </returns>
 /// <typeparam name="T"> The type of item to remove from the collection. </typeparam>
 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 public Collection<T> RemoveAll<T>();
 /// <summary>
 ///  Gets the type of an item contained in the collection. 
 /// </summary>
 /// 
 /// <returns>
 ///  Object specified in the collection  <paramref name="item"/>  The type of. 
 /// </returns>
 /// <param name="item"> The item in the collection for which the type is to be retrieved. </param><exception cref="T:System.ArgumentNullException"><paramref name="item"/>  For  null . </exception>
 [__DynamicallyInvokable]
 protected override Type GetKeyForItem(TItem item);
 /// <summary>
 ///  Insert at a specific position in the collection 1 Elements. 
 /// </summary>
 /// <param name="index"> The zero-based index where you should insert  <paramref name="item"/> . </param><param name="item"> The object to insert into the collection. </param><exception cref="T:System.ArgumentNullException"><paramref name="item"/>  For  null . </exception>
 [__DynamicallyInvokable]
 protected override void InsertItem(int index, TItem item);
 /// <summary>
 ///  Use 1 A new object replaces the item at the specified index. 
 /// </summary>
 /// <param name="index"> To replace  <paramref name="item"/>  Gets or sets the zero-based index of. </param><param name="item"> The object to add to the collection. </param><exception cref="T:System.ArgumentNullException"><paramref name="item"/>  For  null . </exception>
 [__DynamicallyInvokable]
 protected override void SetItem(int index, TItem item);
 }

2. Generic interfaces and generic delegates:

The main function of generics is to define reference types and reference types of generics. A reference type or value type can implement a generic interface by specifying a type argument, or by keeping the type argument in an unspecified state.

See generic interface IEnumerable under 1: Expose enumerator, which supports simple iteration on non-generic collections.


 [ComVisible(true)]
 [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
 [__DynamicallyInvokable]
 public interface IEnumerable
 {
 /// <summary>
 ///  Return 1 An enumerator that iterates through the collection. 
 /// </summary>
 /// 
 /// <returns>
 /// 1 Object that can be used to iterate through the collection  <see cref="T:System.Collections.IEnumerator"/>  Object. 
 /// </returns>
 /// <filterpriority>2</filterpriority>
 [DispId(-4)]
 [__DynamicallyInvokable]
 IEnumerator GetEnumerator();
 }

CLR supports generic delegates to ensure that objects of any type can be passed to a callback method in a type-safe manner. Generic delegates allow 1 child type instance to be passed to 1 callback method without any boxing. Delegation timing provides only four methods: a constructor, an Invlke method, an BeginInvoke method, and an EndInvoke method. If a delegate type is defined that specifies a type parameter, the compiler defines the method of the delegate class, replacing the parameter type and value type of the method with the specified type parameter.

The above is a brief understanding of generic classes, generic interfaces and generic delegates. The purpose of this article is mainly to explain generic methods. Let's learn more about generic generics.

3. Generic method resolution:

1. Overview of generic methods:

When defining a generic class, struct, or interface, any method defined in a type can reference one type parameter specified by the type. A type parameter can be used as an argument to a method, as a return value of a method, or as a local variable defined inside a method. CLR allows a method to specify its own type parameters, which can be used for parameters, return values, or local variables.

The C # compiler supports type inference when a generic method is called. When performing type inference, C # uses the data type of the variable instead of the actual type of the object referenced by the variable. A type can define multiple methods, one of which accepts a specific data type and the other accepts generic type parameters.

Examples of generic methods:


 List<TOutput> ConverAll<TOutput>(Conver<T,TOutput> conv)

List<TOutput> Return type ( 1 Generic list). 

ConverAll Method name. 

<TOutput> Type parameter. 

Conver<T,TOutput> Parameter type (generic delegate). 

conv Parameter name. 

For the above sample code analysis, you need to master: use a different type for each type parameter and apply these type parameters as a whole.

(1). Replace the inclusion method (List) first < T > The T section of), such as replacing T with string:

List<TOutput> ConverAll<TOutput>(Conver<string,TOutput> conv)

(2) After processing T, TOutput needs to be processed again. It can be seen that it is a method type parameter, and guid is used here to replace TOutput.

List<Guid> ConverAll(Conver<string,Guid> conv)

After you assign type arguments to TOutput, you can remove type parameters from your life < TOutput > Call the method a non-generic method, as above. The above example can process a string list and use a converter to generate an Guid list.

Converts each element in the original list to the target type, adds the converted element to a list, and returns the list. The above processing methods mainly refine the parameters of generic methods one by one. No matter what discipline, it is necessary to simplify complex problems and concretize abstract problems, which is also a common processing method.

2. Type constraints:

Constraints limit the number of types that can be specified as generic arguments. By limiting the number of types, we can perform more operations on those types. A constraint can be applied to a type parameter of a generic type or to a type parameter of a generic method. CLR does not allow overloading based on type parameter names or constraints, and can only overload types or methods based on the number of elements. You are not allowed to specify any constraints on type parameters of overriding methods, but the names of type arguments can be changed.

The operation of a generic constraint to be placed at the end of a generic method or generic type declaration and introduced by context-critical where.

(1). Reference type constraints:

Reference type constraint: Used to ensure that the type argument used is a reference type. (Represented as: T: class, and the first constraint that must be specified for the type parameter.)

(2). Value type constraints:

Value type constraint: Used to ensure that the type parameter used refers to the type. (Represented as: T: struct, nullable types are not included)

(3). Constructor type constraints:

Construct correspondence type constraint: Specifies the last constraint of all type parameters, which checks whether the type argument has a parameterless constructor that can be used to create instances. (Represented as: T: new ()) applies to all value types, all non-static, non-abstract classes that do not display a declared constructor, and all non-abstract classes that display a common parameterless constructor.

(4). Conversion type constraints:

Conversion Type Constraint: Allows you to specify another type to which type arguments must be implicitly converted by a 1-type, reference, or box-boxing conversion. You can also specify that a type argument must be convertible to another type argument. (Example: class Sample) < T > where T: Stream)

(5). Combination constraints:

Combination constraint: A constraint in which each constraint is combined from 1, but the combination constraint also has restrictions. Because no type is both a reference type and a value type. Because there is a parameterless constructor for every 1 value, it is not allowed to specify another constructor constraint if there is already one value type constraint. If there are multiple type constraints, and one of them is a class, it should appear before the interface, and we cannot specify the same interface more than once. Different type parameters can be introduced by one where with different constraints.

Note: Type inference is only applicable to generic methods, not generic types.

The above is a simple analysis of the related concepts and constraints of generic methods. Next, look at the concrete implementation of some distribution methods in NET:


 /// <summary>
 ///  Encapsulation 1 Methods with 4 Parameters and does not return a value. 
 /// </summary>
 /// <param name="arg1"> Of the method encapsulated by this delegate 1 Parameters. </param><param name="arg2"> Of the method encapsulated by this delegate 2 Parameters. </param><param name="arg3"> Of the method encapsulated by this delegate 3 Parameters. </param><param name="arg4"> Of the method encapsulated by this delegate 4 Parameters. </param><typeparam name="T1"> Of the method encapsulated by this delegate 1 Parameter types. </typeparam><typeparam name="T2"> Of the method encapsulated by this delegate 2 Parameter types. </typeparam><typeparam name="T3"> Of the method encapsulated by this delegate 3 Parameter types. </typeparam><typeparam name="T4"> Of the method encapsulated by this delegate 4 Parameter types. </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>
 ///  Indicate the comparison is the same 1 Gets or sets the methods of two objects of type. 
 /// </summary>
 /// 
 /// <returns>
 /// 1 Signed integer indicating  <paramref name="x"/>  And  <paramref name="y"/>  As shown in the following table.   Value   Meaning   Less than  0 <paramref name="x"/>  Less than  <paramref name="y"/> .  0 <paramref name="x"/>  Equal to  <paramref name="y"/> .   Greater than  0 <paramref name="x"/>  Greater than  <paramref name="y"/> . 
 /// </returns>
 /// <param name="x"> The number to compare 1 Objects. </param><param name="y"> The number to compare 2 Objects. </param><typeparam name="T"> The type of object to compare. </typeparam><filterpriority>1</filterpriority>
 [__DynamicallyInvokable]
 public delegate int Comparison<in T>(T x, T y);

4. Generic method application code example:

For the above explanation about generic methods, here is a code for generic methods to operate XML:


 /// <summary>
 ///  Generic methods: the compiler can infer type parameters from the passed-in method parameters; It cannot infer type parameters only from constraints or return values 
 /// </summary>
 public class ObjectXmlSerializer
 {
 /// <summary>
 ///  Deserialization of files 
 /// </summary>
 /// <typeparam name="T"> Return value type </typeparam>
 /// <param name="fileName"></param>
 /// <returns>
 ///  If the log is enabled, when an exception occurs, the exception is written to the log; If the log is not opened, the exception information is directly thrown out 
 /// 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>
 ///  The file is deserialized. If an exception occurs, the exception information is written into the log 
 /// </summary>
 /// <typeparam name="T"> Load the type of class </typeparam>
 /// <param name="fileName"> File name </param>
 /// <param name="loggingEnabled"> Enable logging </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);
  // Deserialization object 
  return (T)serializer.Deserialize(fs);
  }
  catch (Exception e)
  {
  if (loggingEnabled)
  {
   // File exception, write to log 
   LogLoadFileException(fileName, e);
   return null;
  }
  else
  {
   throw new Exception(e.Message);
  }
  }
  finally
  {
  if (fs != null) fs.Close();
  }
 }
 /// <summary>
 ///  Serialization 1 Objects to a file .
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="fileName"> Filename </param>
 /// <param name="data"> Data to be serialized </param>
 /// <returns>
 ///  If the log is enabled, when an exception occurs, the exception is written to the log; If the log is not opened, the exception information is directly thrown out 
 /// 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>
 ///  The file is deserialized. If an exception occurs, the exception information is written into the log 
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="fileName"> Filename </param>
 /// <param name="data"> Send serialized object </param>
 /// <param name="loggingEnabled"> Do you want to enable logging </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);
  // Serialize object 
  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>
 ///  Serialization 
 /// XML & Datacontract Serialize & Deserialize Helper
 /// </summary>
 /// <typeparam name="T">T Specifies that the class Type </typeparam>
 /// <param name="serialObject"></param>
 /// <returns></returns>
 public static string XmlSerializer<T>(T serialObject) where T : class
 {
  var ser = new XmlSerializer(typeof(T));
  //MemoryStream Realize reading and writing to memory instead of reading and writing to persistent memory 
  //MemoryStream Encapsulates data stored in the form of an unsigned byte array that is created when the MemoryStream Object is initialized when the, 
  // Or the array can be created as an empty array. These encapsulated data can be accessed directly in memory. 
  // Memory flow can reduce the need for temporary buffers and temporary files in applications. 
  var mem = new MemoryStream();
  var writer = new XmlTextWriter(mem, UTF8);
  ser.Serialize(writer, serialObject);
  writer.Close();
  return UTF8.GetString(mem.ToArray());
 }
 /// <summary>
 ///  Deserialization 
 /// </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> The return value type is the type passed in </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>
 ///  Exception log on serialization 
 /// </summary>
 /// <param name="fileName"> Filename </param>
 /// <param name="ex"> Anomaly </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);
  // Write to log record method 
  // Logger.LogEvent(LogCategory, LogEventLoadFileException, sb.ToString());
 }
 /// <summary>
 ///  Exception log on deserialization 
 /// </summary>
 /// <param name="fileName"> Filename </param>
 /// <param name="ex"> Anomaly </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>
 ///  Will xml A string is serialized into a data stream encoded as ASCII,UTF8 ) 
 /// </summary>
 /// <returns> String to stream </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();
  }
 }
 }

The above code will not be repeated, and those that need secondary code can be used.

5. Summary:

This article explains the generic knowledge introduced by C # 2.0, which mainly includes generic classes, generic interfaces and generic delegates, and focuses on generic methods and constraint classification of generics. Finally, some methods of operating xml by using generic methods are given. I hope the above explanation can help those who want to learn.


Related articles: