Home  >  Article  >  Backend Development  >  C# extension method analysis

C# extension method analysis

黄舟
黄舟Original
2017-02-07 15:05:181117browse

In the process of project development using object-oriented languages, the "inheritance" feature is often used, but not all scenarios are suitable for using the "inheritance" feature. There are also some basic principles of design patterns. More mentions.

Problems caused by the use of inheritance-related features: The inheritance relationship of objects is actually defined at compile time, so the implementation inherited from the parent class cannot be changed at runtime. The implementation of a subclass is so closely dependent on its parent class that any changes in the parent class's implementation will inevitably cause changes in the subclass.

When you need to reuse a subclass, if the inherited implementation is not suitable to solve the new problem, the parent class must rewrite it or be replaced by other more suitable classes. This dependency limits flexibility properties and ultimately limits reusability. As an alternative to inheriting features, most will adopt the principle of composition/aggregation reuse, "Principle of composition/aggregation reuse": try to use composition/aggregation and try not to use class inheritance.

Sometimes it may not be appropriate to use inheritance features when objects of a new type should carry details about additional behavior, for example when dealing with reference types, sealed classes, or interfaces. When facing these requirements, we sometimes write some static classes containing some static methods. But too many static methods will cause additional unnecessary overhead.

1. Overview of extension methods:

Faced with the above problems about "inheritance" and when facing some needs of the project, the way we need to solve these problems is "extension methods" ". "Extension methods" were introduced in C# 3.0, which not only have the advantages of static methods, but also improve the readability of the code that calls them. When using extension methods, you can call static methods just like instance methods.

1. Basic principles of extension methods:

(1).C# only supports extension methods and does not support extended attributes, extended events, extended operators, etc.

(2). Extension methods (methods whose first parameter is preceded by this) must be declared in a non-generic static class. The extension method must have one parameter, and only the first parameter is marked with this. .

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

(4).C# compilation requires "importing" extension methods. (Static methods can be named arbitrarily. When the C# compiler is looking for methods, it takes time to search. 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 method.

(6). When using an extension method to extend a type, 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 is 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 a pointer type.

In the above two classification descriptions, a brief introduction is given to the basic characteristics and declaration methods of extension methods. The usage of extension methods will be shown in the following code samples. Again, No further explanation.

2. Extension method principle analysis:

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

C# Once the first parameter of a static method is marked with the this keyword, the compiler will internally apply a customized attribute to the method. This attribute will be included in the metadata of the final generated file. For persistent storage, this property is 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 that contains at least one static class that meets the above characteristics will also be applied to its metadata. This attribute. If the code refers to an instance method that does not exist, the compiler will quickly scan all referenced assemblies to determine which of them contain extension methods. Then, within this assembly, it 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 only use the extension method in one of the classes. To use a type by its simple name (without the named control prefix), you can import all namespaces in which the type exists, but when you do this, you have no way to prevent extension methods in that namespace from being imported as well.

3..NET3.5 extension methods Enumerable and Queryable:

In the framework, the biggest purpose of extension methods is to serve LINQ. The framework provides auxiliary extension methods, located in System. Enumerable and Queryable classes under the Linq namespace. Most extensions of Enumerable are IEnumerable8742468051c85b06f0a0af9e3e506b5c, and most extensions of Queryable are IQueryable8742468051c85b06f0a0af9e3e506b5c.

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():对集合进行过滤的一个方式,接受一个谓词,并将其应用于原始集合中的每个元素。

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; 
}


以上分别介绍了Range()和Where()两个方法,该类中还主要包含select()、orderby()等等方法。


2.Queryable类中的常用方法:


(1).IQueryable接口:


/// <summary>
  /// 提供对未指定数据类型的特定数据源的查询进行计算的功能。
  /// </summary>
  /// <filterpriority>2</filterpriority>
  public interface IQueryable : IEnumerable
  {
    /// <summary>
    /// 获取与 <see cref="T:System.Linq.IQueryable"/> 的实例关联的表达式目录树。
    /// </summary>
    /// 
    /// <returns>
    /// 与 <see cref="T:System.Linq.IQueryable"/> 的此实例关联的 <see cref="T:System.Linq.Expressions.Expression"/>。
    /// </returns>
    Expression Expression { get; }
    /// <summary>
    /// 获取在执行与 <see cref="T:System.Linq.IQueryable"/> 的此实例关联的表达式目录树时返回的元素的类型。
    /// </summary>
    /// 
    /// <returns>
    /// 一个 <see cref="T:System.Type"/>,表示在执行与之关联的表达式目录树时返回的元素的类型。
    /// </returns>
    Type ElementType { get; }
    /// <summary>
    /// 获取与此数据源关联的查询提供程序。
    /// </summary>
    /// 
    /// <returns>
    /// 与此数据源关联的 <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) }
                    ));
}


以上是对扩展方法中两个类进行了一个简单的解析。


四.扩展方法实例:


由于扩展方法实际是对一个静态方法的调用,所以CLR不会生成代码对调用方法的表达式的值进行null值检查


1.异常处理代码:


  /// <summary>
    /// 为参数验证提供有用的方法
    /// </summary>
    public static class ArgumentValidator
    {
        /// <summary>
        /// 如果argumentToValidate为空,则抛出一个ArgumentNullException异常
        /// </summary>
        public static void ThrowIfNull(object argumentToValidate, string argumentName)
        {
            if (null == argumentName)
            {
                throw new ArgumentNullException("argumentName");
            }
            if (null == argumentToValidate)
            {
                throw new ArgumentNullException(argumentName);
            }
        }
        /// <summary>
        /// 如果argumentToValidate为空,则抛出一个ArgumentException异常
        /// </summary>
        public static void ThrowIfNullOrEmpty(string argumentToValidate, string argumentName)
        {
            ThrowIfNull(argumentToValidate, argumentName);
            if (argumentToValidate == string.Empty)
            {
                throw new ArgumentException(argumentName);
            }
        }
        /// <summary>
        /// 如果condition为真,则抛出ArgumentException异常
        /// </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>
        /// 如果指定目录存在该文件则抛出FileNotFoundException异常
        /// </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("&#39;{0}&#39; not found".Fi(fileSytemObject.FullName));
            }
        }
        public static string Fi(this string format, params object[] args)
        {
            return FormatInvariant(format, args);
        }
        /// <summary>
        /// 格式化字符串和使用<see cref="CultureInfo.InvariantCulture">不变的文化</see>.
        /// </summary>
        /// <remarks>
        /// <para>这应该是用于显示给用户的任何字符串时使用的“B”>“B”>“”。它意味着日志
        ///消息,异常消息,和其他类型的信息,不使其进入用户界面,或不会
        ///无论如何,对用户都有意义;).</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>
        /// 如果时间不为DateTimeKind.Utc,则抛出ArgumentException异常
        /// </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.枚举扩展方法:

public static class EnumExtensions
    {
        /// <summary>
        /// 获取名字
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static string GetName(this Enum e)
        {
            return Enum.GetName(e.GetType(), e);
        }
        /// <summary>
        /// 获取名字和值
        /// </summary>
        /// <param name="enumType">枚举</param>
        /// <param name="lowerFirstLetter">是否转化为小写</param>
        /// <returns></returns>
        public static Dictionary<string, int> GetNamesAndValues( this Type enumType, bool lowerFirstLetter)
        {
//由于扩展方法实际是对一个静态方法的调用,所以CLR不会生成代码对调用方法的表达式的值进行null值检查
            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;
        }
        /// <summary>
        /// 转换为小写
        /// </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);
        }
    }

五.总结:

在本文中,主要对扩展方法进行了一些规则说明、声明方式,使用方式,以及对扩展方法的意义和扩展方法的原理进行了简单的解答。并在本文的最后给了一个枚举的扩展方法代码。

以上就是C#的扩展方法解析的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn