This is the 27th day of my participation in the More Text Challenge. For more details, see more text Challenge
In the process of using object-oriented language for project development, the “inheritance” feature is often used, but not all scenarios are suitable for using “inheritance” feature, and some basic principles of design patterns are also mentioned.
The problem with the use of inheritance features is that the inheritance of an object is defined at compile time, so there is no way to change the implementation of inheritance from a parent class at run time. The implementation of a child class is so closely dependent on its parent class that any change in the implementation of the parent class necessarily results in a change in the child class. When you need to reuse subclasses, if the inherited implementation is not suitable to solve the new problem, the parent class must override it or be replaced by another class that is more suitable, a dependency that limits flexibility and ultimately reusability. As an alternative to inheritance, the most common approach is to use the synthesis/aggregation reuse principle, the “Synthesis/aggregation reuse principle” : use synthesis/aggregation whenever possible, and try not to use class inheritance.
If an object of a new type should carry details about the additional behavior, it may not be appropriate to use inherited features, such as when dealing with a specific type, a sealed class, or an interface. In the face of these requirements, we sometimes write static classes that contain static methods. However, too many static methods can cause extra unnecessary overhead.
I. Overview of extension methods:
In the face of the above “inheritance” problems, and in the face of some of the requirements of the project, the way we need to solve these problems is the “extension approach.” In C#3.0, “extension methods” were introduced, which have the benefits of static methods while improving the readability of the code that calls them. When using extension methods, static methods can be called just as instance methods can be called.
1. Basic principles of the extension method:
(1).C# only supports extension methods, not extended properties, extended events, extended operators, etc.
(2). Extension methods (methods whose first argument is preceded by this) must be declared in a non-generic static class. Extension methods must take one argument, and only the first argument uses the this flag.
(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 as much as they want. The C# compiler takes time to find a method. It needs to 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 methods.
(6). When an extension method extends a type, it also extends derived types.
2. Extension method declaration:
(1). Must be in a non-nested, non-generic static class (therefore must be a static method)
(2). There is at least one parameter.
(3). The first argument must be prefixed with this keyword.
(4). The first argument must not have any other modifiers (such as ref or out).
(5). The type of the first argument cannot be a pointer.
In the above two categories of instructions, the basic features of the extension method and the way to declare a simple introduction, the use of the extension method, will be shown in the following code samples, again no more explanation.
2. Extension method principle analysis:
“Extension methods” are unique to C# and use the attribute ExtensionAttribute within an extension method.
Once C# has marked the first argument of a static method with the this keyword, the compiler internally applies a custom attribute to the method. This attribute will be stored persistently in the metadata of the resulting file. This attribute is in the System.
This attribute is also applied to the metadata of any static class that contains at least one extension method, and to the metadata of any assembly that contains at least one static class that conforms to the above characteristics. If the code uses an instance method that does not exist, the compiler quickly scans all referenced assemblies to determine which of them contain the extension method. Then, within that assembly, it can scan for static classes that contain the extension method.
If two classes in the same namespace contain methods of the same extension type, there is no way to use only the extension methods in one of the classes. To use a type by its simple name (without a namespace prefix), you can import all namespaces of that type, but in doing so, you have no way to prevent the extension methods in that namespace from being imported as well.
Three.. NET3.5 Extension methods Enumerable and Queryable
The framework provides auxiliary extension methods, Enumerable and Queryable, in the System. LINQ namespace. Enumerable Most extensions are IEnumerable,Queryable most extensions are IQueryable.
1. Common methods in Enumerable:
(1).range (): One argument is the start number and the other is the number of results to generate.
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;
}
Copy the code
(2).where (): a way of filtering a set by taking a predicate and applying 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;
}
Copy the code
The Range() and Where() methods are described above, and the class also includes select(), orderby(), and so on.
2. Common methods in the Queryable class
(1). IQueryable interface:
/// <summary> /// Provides the ability to calculate queries for a specific data source with no specified data type. /// </summary> /// <filterpriority>2</filterpriority> public interface IQueryable : IEnumerable {/// <summary> /// Retrieves the expression directory tree associated with the instance <see cref="T: System.linq.iQueryable "/>. /// </summary> /// /// <returns> /// The <see > associated with the <see cref="T: system.linq.iQueryable "/> instance Which = "T: System. Linq Expressions. The Expression" / >. /// </returns> Expression Expression { get; } /// <summary> /// Gets the type of elements returned when the expression tree associated with this instance <see cref="T: system.linq.iQueryable "/> is executed. /// </summary> /// /// <returns> /// A <see cref="T: system. Type"/> that represents the Type of the element returned when the associated expression directory tree is executed. /// </returns> Type ElementType { get; } /// <summary> /// Gets the query provider associated with this data source. / / / < summary > / / / / / / < returns > / / / data associated with this < see which = "T: System. Linq. IQueryProvider" / >. /// </returns> IQueryProvider Provider { get; }}Copy the code
(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) }
));
}
Copy the code
(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) }
));
}
Copy the code
This is a simple resolution of two classes in the extension method.
4. Extension method examples:
Since an extension method is actually a call to a static method, the CLR does not generate code to null-check the value of the expression that calls the method
1. Exception handling code:
<summary> public static class ArgumentValidator {/// <summary> /// ArgumentValidator {/// <summary> /// If argumentToValidate is empty, /// </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, /// </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, </param> <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); <param name="fileSytemObject"></param> <param name="fileSytemObject"> /// <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 > / / / a formatted string and use the < see which = "CultureInfo. InvariantCulture" > constant culture < / see >. / / / < summary > / / / < few > / / / <para> This should be the "B" > "B" > "" used when displaying any string to the user. It means logging /// messages, exception messages, and other types of information that do not enter the user interface, or do not /// make sense to the user anyway;) .</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, /// </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); }}}Copy the code
2. Enumeration extension method:
Public static class EnumExtensions {// <summary> // <param name="e"></param> /// <returns></returns> public static string GetName(this Enum e) { return Enum.GetName(e.GetType(), e); } / / / < summary > / / / / / / for name and values < summary > / / / < param name = "enumType" > enumeration < param > / / / < param Name ="lowerFirstLetter"> </param> </returns> public static Dictionary<string, Int \> GetNamesAndValues(this Type enumType, bool lowerFirstLetter) { So the CLR will not generate code to call a method of the value of the expression of null check ArgumentValidator. ThrowIfNull (enumType, "enumType"); Var names = Enum.GetNames(enumType); 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; } /// <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); }}Copy the code
V. Summary:
In this paper, the main extension method for some rules, declaration, use, and the meaning of the extension method and the principle of the extension method for a simple solution. And at the end of this article gives an enumeration of the extension method code.
jue