Analysis of C # extension method,
In the process of project development using object-oriented language, many will use the "inheritance" feature, but not all scenarios are suitable for the "inheritance" feature, some basic principles of the design pattern are also mentioned.
Problems arising from the use of inheritance-related features: the inheritance relationships of objects are defined at compilation, so the implementation of inheritance from the parent class cannot be changed at runtime. The implementation of a subclass is closely related to its parent class, so that any changes in the implementation of the parent class will inevitably lead to changes in the subclass. When you need to reuse child classes, if the inherited implementation is not suitable for solving new problems, the parent class must be rewritten or replaced by other more suitable classes, this dependency limits flexibility and ultimately limits reusability. The inheritance feature can be replaced by the synthesis/aggregation Reuse Principle. "synthesis/aggregation Reuse Principle": Use synthesis/aggregation whenever possible, and avoid class inheritance whenever possible.
If an object of the new type should carry details about the additional behavior, it may not be appropriate to use the inheritance feature, for example, when processing the type, sealing class, or interface. In the face of these requirements, we sometimes write some static classes that contain some static methods. However, excessive static methods may cause additional unnecessary overhead.
1. Overview of extension methods:
In the face of the above problems related to "inheritance" and some project requirements, we need to solve these problems by "expansion method ". The "Extension Method" is introduced in C #3.0, which has both the advantages of static methods and the readability of the Code that calls them. When using an extension method, you can call a static method as you call an instance method.
1. Basic principles of extension methods:
(1). C # only supports extension methods, but does not support extension attributes, Extension events, and extension operators.
(2 ). the extension method (the first parameter is the this method) must be declared in a non-generic static class. The extension method must have a parameter, and only the first parameter uses the this flag.
(3). C # When the compiler finds extension methods in static classes, these static classes must have file scopes.
(4). C # The "import" extension method is required for compilation. (Static methods can be named at will. C # The Compiler needs to spend time searching for methods and check all static classes in the file scope, and scan all their static methods to find a match)
(5). Multiple Static classes can define the same extension method.
(6). When a type is extended using an extension method, the derived type is also extended.
2. Extension Method declaration:
(1). It must be in a non-nested, non-generic static class (so it must be a static method)
(2). There must be at least one parameter.
(3). The first parameter must be prefixed with the this keyword.
(4). the first parameter cannot have any other modifiers (such as ref or out ).
(5). the type of the first parameter cannot be pointer type.
In the preceding two categories, the basic features and declaration methods of the extension method are briefly introduced, it will be displayed in the code sample below, and I will not explain it again.
Ii. Analysis of extension methods:
The "Extension Method" is a unique method of C #. The ExtensionAttribute is used in the extension method.
C # once the this keyword is used to mark the first parameter of a static method, the compiler will apply a custom attribute to the method internally, this attribute is stored persistently in the metadata of the final generated file. This attribute is stored in the System. core dll assembly.
As long as any static class contains at least one extension method, this attribute will also be applied to its metadata. Any Assembly contains at least one static class that meets the preceding features, this attribute is also applied to its metadata. If the code consumer uses a non-existent instance method, the compiler will quickly scan all referenced assembly, determine which of them contain the extension method, and then, in this Assembly, you can scan static classes that contain extension methods.
If two classes in the same namespace contain methods with the same extension type, there is no way to use only the extension methods in one of the classes. To use a type by using a simple name (without the naming control prefix), you can import all namespaces of the type, you cannot prevent the extension methods in that namespace from being imported.
Iii. NET3.5 extension methods Enumerable and Queryable:
In the Framework, the maximum purpose of the extension method is to serve the LINQ service. The framework provides an auxiliary extension method, which is located in the Enumerable and Queryable classes under the System. Linq namespace. Most Enumerable extensions are IEnumerable <T>, while most Queryable extensions are IQueryable <T>.
1. Common methods in the Enumerable class:
(1). Range (): one parameter is the starting number, and the other 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 (): a way to filter a set, accept a predicate, and apply it to each element in the original set.
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; }
The preceding describes the Range () and Where () methods, including select (), orderby (), and so on.
2. Common methods in the Queryable class:
(1). IQueryable interface:
/// <Summary> /// provides the function of calculating the query of a specific data source that does not specify a data type. /// </Summary> /// <filterpriority> 2 </filterpriority> public interface IQueryable: IEnumerable {/// <summary> // obtain and <see cref = "T: System. linq. IQueryable "/>. /// </Summary> ///// <returns> /// and <see cref = "T: System. linq. <see cref = "T: System. linq. expressions. expression "/>. /// </Returns> Expression {get ;}/// <summary> // obtain the information in execution and <see cref = "T: System. linq. the type of the element returned when the expression directory tree associated with this instance is IQueryable "/>. /// </Summary> ///// <returns> /// A <see cref = "T: System. type "/>, indicating the Type of the elements returned when executing the associated expression directory tree. /// </Returns> Type ElementType {get ;}/// <summary> // obtain the query provider associated with the data source. /// </Summary> ///// <returns> // <see cref = "T: System. Linq. IQueryProvider"/> associated with the data source. /// </Returns> IQueryProvider {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 analysis of the two classes in the extension method.
4. Extension Method example:
Because the extension method actually calls a static method, the CLR does not generate code to check the null value of the expression value of the method to be called.
1. Exception Handling Code:
/// <Summary> /// provides a useful method for parameter verification. // </summary> public static class ArgumentValidator {// <summary> /// if argumentToValidate is null, an ArgumentNullException exception is thrown. // </summary> public static void ThrowIfNull (object argumentToValidate, string argumentName) {if (null = argumentName) {throw new ArgumentNullException ("argumentName");} if (null = argumentToValidate) {throw new ArgumentNullException (argu MentName) ;}/// <summary> /// if argumentToValidate is null, an ArgumentException exception is thrown /// </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, an ArgumentException exception is thrown. /// </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> /// if the specified directory exists, the FileNotFoundException exception is thrown. /// </summary> /// <param name = "fileSytemObject"> </param> /// <param name = "argumentName"> </param> public static void ThrowIfDoesNotExist (F IleSystemInfo 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> /// format the string and use <see cref = "CultureInfo. invariantCulture "> constant culture </see>. /// </summary> /// <remarks> /// <para> This is the "B"> "B"> used to display any string to the user. "". It means that the log // message, exception message, and other types of information do not make it into the user interface, or it does not // whatever, it makes sense to the user ;). </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, an ArgumentException exception is thrown. /// </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. Enumeration extension method:
Public static class EnumExtensions {// <summary> // obtain the name /// </summary> /// <param name = "e"> </param> // /<returns> </returns> public static string GetName (this Enum e) {return Enum. getName (e. getType (), e );} /// <summary> /// obtain the name and value /// </summary> /// <param name = "enumType"> enumeration </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 actually calls a static method, therefore, CLR does not generate code to check ArgumentValidator for the null value of the expression that calls the method. throwIfNull (enumType, "enumType"); // obtain the enumerated name array var names = Enum. getNames (enumType); // obtain the 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 );}}
V. Summary:
In this article, we mainly explain some rules, declaration methods, usage methods of the extension method, and give a simple answer to the meaning of the extension method and the principle of the extension method. At the end of this article, we provide an enumeration extension method code.