Extension method of analyzing C

  • 2021-11-14 06:50:31
  • OfStack

In the process of using object-oriented language for project development, the feature of "inheritance" is often used, but not all scenarios are suitable for using the feature of "inheritance", which is also mentioned in some basic principles of design patterns.

Problems with the use of inheritance-related features: The inheritance relationship of an object is defined at compile time, so it is impossible to change the implementation inherited from the parent class at run time. The implementation of a subclass is so closely dependent on its parent that any change in the implementation of the parent class will inevitably lead to a change in the subclass. When you need to reuse a subclass, if the inherited implementation is not suitable for solving a new problem, the parent class must rewrite it or be replaced by another more suitable class. This dependency limits flexibility and ultimately reusability. Instead of inheriting features, most of them will adopt the principle of composition/aggregation reuse, "composition/aggregation reuse principle": try to use composition/aggregation, and try not to use class inheritance.

If new types of objects should carry details about additional behavior, it may sometimes be inappropriate to use inheritance features, for example, when dealing with reference types, sealed classes, or interfaces. When faced with these requirements, we sometimes write 1 static class containing 1 static method. However, too many static methods will cause extra unnecessary overhead.

1. Overview of extension methods:

In the face of the above problems related to "inheritance", and in the face of some requirements of the project, the way we need to solve these problems is "extension method". The introduction of "extension methods" in C # 3.0 has both the advantages of static methods and the readability of the code that calls them. When using extension methods, you can call static methods just like instance methods.

1. The basic principles of the extension method:

(1). C # only supports extension methods, not extension properties, extension events, extension operators, etc.

(2). The extension method (the first parameter is preceded by the method of this) must be declared in a non-generic static class, the extension method must have one parameter, and only the first parameter uses the this tag.

(3). When the C # compiler looks for extension methods in static classes, it requires that the static classes themselves have file scope.

(4). C # Compilation requires "importing" extension methods. (Static methods can be named arbitrarily, and the C # compiler takes time to find methods, checking all static classes in the scope of the file and scanning all their static methods for a match.)

(5). Multiple static classes can define the same extension method.

(6). When you extend a type with an extension method, you also extend the derived type.

2. Extend the method declaration:

(1). Must be in a non-nested, non-generic static class (so must be a static method)

(2). Has at least 1 parameter.

(3). The first parameter must be prefixed with this keyword.

(4). The 1st parameter cannot have any other modifiers (such as ref or out).

(5). The type of the 1st parameter cannot be a pointer type.

In the above two classification descriptions, the basic characteristics and declaration methods of extension methods are briefly introduced. The use methods of extension methods will be shown in the following code samples, and no more explanations will be made again.

2. Analysis of the principle of expansion method:

"Extension method" is a unique method of C #, and ExtensionAttribute, attribute, will be used in extension method.

C # 1 Once the first parameter of a static method is marked with the this keyword, the compiler internally applies a custom attribute to the method, which is persistently stored in the metadata of the resulting file. This attribute is in the System. Core dll assembly.

As long as any static class contains at least one extension method, its metadata will also apply this attribute, and any assembly contains at least one static class that meets the above characteristics, its metadata will also apply this attribute. If the code uses an instance method that does not exist, the compiler will quickly scan all the referenced assemblies to determine which ones contain extension methods, and then scan static classes containing extension methods in this assembly.

If two classes in the same namespace have methods of the same extension type, there is no way to use only the extension methods in one of the classes. In order to use a type by its simple name (without a namespace prefix), you can import all the namespaces of the type, but in doing so, there is no way to prevent the extension methods in that namespace from being imported.

3. Extension methods for NET3.5 Enumerable and Queryable:

In the framework, the greatest use of extension methods is to serve LINQ, and the framework provides auxiliary extension methods, Enumerable and Queryable classes located under System. Linq namespace. Most extensions to Enumerable are IEnumerable < T > Most extensions to Queryable are IQueryable < T > .

1. Common methods in the Enumerable class:

(1). Range (): 1 parameter is the starting number and 1 is the number of results to be generated.


  public static IEnumerable<int> Range(int start, int count) { 
   long max = ((long)start) + count - 1;
   if (count < 0 || max > Int32.MaxValue) throw Error.ArgumentOutOfRange("count"); 
   return RangeIterator(start, count);
  }
  static IEnumerable<int> RangeIterator(int start, int count) { 
   for (int i = 0; i < count; i++) yield return start + i;
  }

(2). Where (): One way to filter the collection, taking a predicate and applying it to each element in the original collection.


 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
   if (source == null) throw Error.ArgumentNull("source"); 
   if (predicate == null) throw Error.ArgumentNull("predicate"); 
   if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
   if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); 
   if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
   return new WhereEnumerableIterator<TSource>(source, predicate);
  }
  public WhereEnumerableIterator(IEnumerable<TSource> source, Func<TSource, bool> predicate) { 
    this.source = source;
    this.predicate = predicate; 
   }

Two methods, Range () and Where (), are introduced above, and select (), orderby () and so on are mainly included in this class.

2. Common methods in the Queryable class:

(1). IQueryable interface:


 /// <summary>
 ///  Provides the ability to evaluate queries for specific data sources with no data type specified. 
 /// </summary>
 /// <filterpriority>2</filterpriority>
 public interface IQueryable : IEnumerable
 {
 /// <summary>
 ///  Gets the  <see cref="T:System.Linq.IQueryable"/>  Gets or sets the expression directory tree associated with an instance of. 
 /// </summary>
 /// 
 /// <returns>
 ///  And  <see cref="T:System.Linq.IQueryable"/>  Object associated with this instance of  <see cref="T:System.Linq.Expressions.Expression"/> . 
 /// </returns>
 Expression Expression { get; }
 /// <summary>
 ///  Gets the data that is executed with the  <see cref="T:System.Linq.IQueryable"/>  Gets or sets the type of element returned when the expression tree is associated with this instance of. 
 /// </summary>
 /// 
 /// <returns>
 /// 1 A  <see cref="T:System.Type"/> That represents the type of element returned when the expression tree associated with it is executed. 
 /// </returns>
 Type ElementType { get; }
 /// <summary>
 ///  Gets the query provider associated with this data source. 
 /// </summary>
 /// 
 /// <returns>
 ///  Object associated with this data source  <see cref="T:System.Linq.IQueryProvider"/> . 
 /// </returns>
 IQueryProvider Provider { get; }
 }

(2).Where():


 public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) { 
   if (source == null)
    throw Error.ArgumentNull("source"); 
   if (predicate == null)
    throw Error.ArgumentNull("predicate");
   return source.Provider.CreateQuery<TSource>(
    Expression.Call( 
     null,
     ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource)), 
     new Expression[] { source.Expression, Expression.Quote(predicate) } 
     ));
  }

(3).Select():


  public static IQueryable<TResult> Select<TSource,TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector) {
   if (source == null)
    throw Error.ArgumentNull("source");
   if (selector == null) 
    throw Error.ArgumentNull("selector");
   return source.Provider.CreateQuery<TResult>( 
    Expression.Call( 
     null,
     ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TResult)), 
     new Expression[] { source.Expression, Expression.Quote(selector) }
     ));
  }

The above is a simple parsing of two classes in the extension method.

4. Extend the method instance:

Because the extension method is actually a call to a static method, CLR does not generate code to check the value of the expression calling the method for null value

1. Exception handling code:


 /// <summary>
 ///  Provides a useful method for parameter validation 
 /// </summary>
 public static class ArgumentValidator
 {
  /// <summary>
  ///  If argumentToValidate Is empty, throw 1 A ArgumentNullException Anomaly 
  /// </summary>
  public static void ThrowIfNull(object argumentToValidate, string argumentName)
  {
   if (null == argumentName)
   {
    throw new ArgumentNullException("argumentName");
   }
   if (null == argumentToValidate)
   {
    throw new ArgumentNullException(argumentName);
   }
  }
  /// <summary>
  ///  If argumentToValidate Is empty, throw 1 A ArgumentException Anomaly 
  /// </summary>
  public static void ThrowIfNullOrEmpty(string argumentToValidate, string argumentName)
  {
   ThrowIfNull(argumentToValidate, argumentName);
   if (argumentToValidate == string.Empty)
   {
    throw new ArgumentException(argumentName);
   }
  }
  /// <summary>
  ///  If condition Is true, throw ArgumentException Anomaly 
  /// </summary>
  /// <param name="condition"></param>
  /// <param name="msg"></param>
  public static void ThrowIfTrue(bool condition, string msg)
  {
   ThrowIfNullOrEmpty(msg, "msg");
   if (condition)
   {
    throw new ArgumentException(msg);
   }
  }
  /// <summary>
  ///  Thrown if the file exists in the specified directory FileNotFoundException Anomaly 
  /// </summary>
  /// <param name="fileSytemObject"></param>
  /// <param name="argumentName"></param>
  public static void ThrowIfDoesNotExist(FileSystemInfo fileSytemObject, String argumentName)
  {
   ThrowIfNull(fileSytemObject, "fileSytemObject");
   ThrowIfNullOrEmpty(argumentName, "argumentName");
   if (!fileSytemObject.Exists)
   {
    throw new FileNotFoundException("'{0}' not found".Fi(fileSytemObject.FullName));
   }
  }
  public static string Fi(this string format, params object[] args)
  {
   return FormatInvariant(format, args);
  }
  /// <summary>
  ///  Formatting strings and using <see cref="CultureInfo.InvariantCulture"> An unchanging culture </see>.
  /// </summary>
  /// <remarks>
  /// <para> This should be used when displaying any string to the user B " > " B " > "". It means logs 
  /// Messages, exception messages, and other types of information, do not enter the user interface, or do not 
  /// In any case, it makes sense to users;) .</para>
  /// </remarks>
  public static string FormatInvariant(this string format, params object[] args)
  {
   ThrowIfNull(format, "format");
   return 0 == args.Length ? format : string.Format(CultureInfo.InvariantCulture, format, args);
  }
  /// <summary>
  ///  If the time is not DateTimeKind.Utc Is thrown ArgumentException Anomaly 
  /// </summary>
  /// <param name="argumentToValidate"></param>
  /// <param name="argumentName"></param>
  public static void ThrowIfNotUtc(DateTime argumentToValidate, String argumentName)
  {
   ThrowIfNullOrEmpty(argumentName, "argumentName");
   if (argumentToValidate.Kind != DateTimeKind.Utc)
   {
    throw new ArgumentException("You must pass an UTC DateTime value", argumentName);
   }
  }
 }

2. Enumerate extension methods:


 public static class EnumExtensions
 {
  /// <summary>
  ///  Get the name 
  /// </summary>
  /// <param name="e"></param>
  /// <returns></returns>
  public static string GetName(this Enum e)
  {
   return Enum.GetName(e.GetType(), e);
  }
  /// <summary>
  ///  Get the name and value 
  /// </summary>
  /// <param name="enumType"> Enumerate </param>
  /// <param name="lowerFirstLetter"> Convert to lowercase </param>
  /// <returns></returns>
  public static Dictionary<string, int> GetNamesAndValues( this Type enumType, bool lowerFirstLetter)
  {
   // Because the extension method is actually a 1 Static method calls, so CLR Code is not generated for the value of the expression calling the method null Value check 
   ArgumentValidator.ThrowIfNull(enumType, "enumType");
   // Gets an array of enumeration names 
   var names = Enum.GetNames(enumType);
   // Get an array of enumerated values 
   var values = Enum.GetValues(enumType);
   var d = new Dictionary<string, int>(names.Length);
   for (var i = 0; i < names.Length; i++)
   {
    var name = lowerFirstLetter ? names[i].LowerFirstLetter() : names[i];
    d[name] = Convert.ToInt32(values.GetValue(i));
   }
   return d;
  }
  /// <summary>
  ///  Convert to lowercase 
  /// </summary>
  /// <param name="s"></param>
  /// <returns></returns>
  public static string LowerFirstLetter(this string s)
  {
   ArgumentValidator.ThrowIfNull(s, "s");
   return char.ToLowerInvariant(s[0]) + s.Substring(1);
  }
 }

5. Summary:

In this paper, we mainly explain some rules, declare ways and use ways of the extension method, and give a simple answer to the meaning and principle of the extension method. At the end of this paper, an enumeration extension method code is given.


Related articles: