Heim  >  Artikel  >  Backend-Entwicklung  >  Analyse der C#-Erweiterungsmethode

Analyse der C#-Erweiterungsmethode

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

Im Prozess der Projektentwicklung mit objektorientierten Sprachen wird häufig die Funktion „Vererbung“ verwendet, aber nicht alle Szenarien sind für die Verwendung der Funktion „Vererbung“ geeignet. Es werden auch einige Grundprinzipien von Entwurfsmustern erwähnt .

Probleme, die durch die Verwendung vererbungsbezogener Funktionen verursacht werden: Die Vererbungsbeziehung von Objekten wird tatsächlich zur Kompilierungszeit definiert, sodass die von der übergeordneten Klasse geerbte Implementierung zur Laufzeit nicht geändert werden kann. Die Implementierung einer Unterklasse ist so eng von ihrer übergeordneten Klasse abhängig, dass alle Änderungen an der Implementierung der übergeordneten Klasse unweigerlich Änderungen in der Unterklasse nach sich ziehen.

Wenn Sie eine Unterklasse wiederverwenden müssen und die geerbte Implementierung nicht zur Lösung des neuen Problems geeignet ist, muss die übergeordnete Klasse sie neu schreiben oder durch andere, besser geeignete Klassen ersetzt werden. Diese Abhängigkeit schränkt die Flexibilitätseigenschaften ein und schränkt sie letztendlich ein Wiederverwendbarkeit. Als Alternative zur Vererbung von Funktionen werden die meisten das Prinzip der Wiederverwendung von Komposition/Aggregation übernehmen, „Prinzip der Wiederverwendung von Komposition/Aggregation“: Verwenden Sie Komposition/Aggregation so weit wie möglich und versuchen Sie, keine Klassenvererbung zu verwenden.

Die Verwendung von Vererbungsfunktionen kann manchmal ungeeignet sein, wenn der neue Objekttyp Details über zusätzliches Verhalten enthalten soll, beispielsweise beim Umgang mit Referenztypen, versiegelten Klassen oder Schnittstellen. Angesichts dieser Anforderungen schreiben wir manchmal einige statische Klassen, die einige statische Methoden enthalten. Aber zu viele statische Methoden verursachen zusätzlichen unnötigen Overhead.

1. Übersicht über Erweiterungsmethoden:

Angesichts der oben genannten Probleme bezüglich der „Vererbung“ und bei einigen Anforderungen des Projekts müssen wir diese Probleme mit „Erweiterungsmethoden“ lösen. ". In C# 3.0 wurden „Erweiterungsmethoden“ eingeführt, die nicht nur die Vorteile statischer Methoden bieten, sondern auch die Lesbarkeit des Codes verbessern, der sie aufruft. Bei der Verwendung von Erweiterungsmethoden können Sie statische Methoden genauso wie Instanzmethoden aufrufen.

1. Grundprinzipien von Erweiterungsmethoden:

(1) C# unterstützt nur Erweiterungsmethoden und keine erweiterten Attribute, erweiterten Ereignisse, erweiterten Operatoren usw.

(2). Erweiterungsmethoden (Methoden, denen dieser Parameter vorangestellt ist) müssen in einer nicht generischen statischen Klasse deklariert werden, und nur der erste Parameter ist damit gekennzeichnet . .

(3) Wenn der C#-Compiler nach Erweiterungsmethoden in statischen Klassen sucht, müssen diese statischen Klassen selbst einen Dateibereich haben.

(4).C#-Kompilierung erfordert das „Importieren“ von Erweiterungsmethoden. (Statische Methoden können beliebig benannt werden. Wenn der C#-Compiler nach Methoden sucht, dauert die Suche einige Zeit. Er muss alle statischen Klassen im Dateibereich überprüfen und alle ihre statischen Methoden scannen, um eine Übereinstimmung zu finden.)

(5). Mehrere statische Klassen können dieselbe Erweiterungsmethode definieren.

(6) Wenn ein Typ mit einer Erweiterungsmethode erweitert wird, wird auch der abgeleitete Typ erweitert.

2. Deklaration der Erweiterungsmethode:

(1) Es muss sich um eine nicht verschachtelte, nicht generische statische Klasse handeln (also eine statische Methode)

(2).Es gibt mindestens einen Parameter.

(3) Dem ersten Parameter muss das Schlüsselwort this vorangestellt werden.

(4) Der erste Parameter darf keine anderen Modifikatoren haben (z. B. ref oder out).

(5) Der Typ des ersten Parameters darf kein Zeigertyp sein.

In den obigen beiden Klassifizierungsbeschreibungen wird eine kurze Einführung in die grundlegenden Merkmale und Deklarationsmethoden von Erweiterungsmethoden gegeben. Die Verwendung von Erweiterungsmethoden wird in den folgenden Codebeispielen erneut erläutert.

2. Analyse des Prinzips der Erweiterungsmethode:

„Erweiterungsmethode“ ist eine für C# einzigartige Methode und das Attribut ExtensionAttribute wird in der Erweiterungsmethode verwendet.

C# Sobald der erste Parameter einer statischen Methode mit dem Schlüsselwort this markiert ist, wendet der Compiler intern ein benutzerdefiniertes Attribut auf die Methode an. Dieses Attribut wird in die Metadaten der endgültig generierten Datei aufgenommen Speicher befindet sich diese Eigenschaft in der System.Core-DLL-Assembly.

Solange eine statische Klasse mindestens eine Erweiterungsmethode enthält, wird dieses Attribut auch auf ihre Metadaten angewendet. Jede Assembly, die mindestens eine statische Klasse enthält, die die oben genannten Merkmale erfüllt, wird auch auf ihre Metadaten angewendet . Dieses Attribut. Wenn der Code auf eine Instanzmethode verweist, die nicht vorhanden ist, scannt der Compiler schnell alle referenzierten Assemblys, um festzustellen, welche davon Erweiterungsmethoden enthalten. Anschließend kann er innerhalb dieser Assembly statische Klassen scannen, die Erweiterungsmethoden enthalten.

Wenn zwei Klassen im selben Namespace Methoden mit demselben Erweiterungstyp enthalten, gibt es keine Möglichkeit, die Erweiterungsmethode nur in einer der Klassen zu verwenden. Um einen Typ anhand seines einfachen Namens (ohne das Namenskontrollpräfix) zu verwenden, können Sie alle Namespaces importieren, in denen der Typ vorhanden ist. Wenn Sie dies tun, können Sie jedoch nicht verhindern, dass auch Erweiterungsmethoden in diesem Namespace importiert werden .

Drei..NET3.5-Erweiterungsmethoden Aufzählbar und abfragbar:

Im Framework besteht der Hauptzweck von Erweiterungsmethoden darin, LINQ zu bedienen. Das Framework stellt zusätzliche Erweiterungsmethoden bereit, die sich im System befinden. Aufzählbare und abfragbare Klassen unter dem Linq-Namespace. Die meisten Erweiterungen von Enumerable sind IEnumerable8742468051c85b06f0a0af9e3e506b5c und die meisten Erweiterungen von Queryable sind IQueryable8742468051c85b06f0a0af9e3e506b5c.

1. Gängige Methoden in der Enumerable-Klasse

(1).Range(): Ein Parameter ist die Startnummer und der andere ist die Anzahl der zu generierenden Ergebnisse.

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)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn