Maison  >  Article  >  développement back-end  >  Analyse de la méthode d'extension C#

Analyse de la méthode d'extension C#

黄舟
黄舟original
2017-02-07 15:05:181117parcourir

Dans le processus de développement de projets utilisant des langages orientés objet, la fonctionnalité « héritage » est souvent utilisée, mais tous les scénarios ne sont pas adaptés à l'utilisation de la fonctionnalité « héritage ». Il existe également quelques principes de base des modèles de conception. .

Problèmes causés par l'utilisation de fonctionnalités liées à l'héritage : La relation d'héritage des objets est en fait définie au moment de la compilation, de sorte que l'implémentation héritée de la classe parent ne peut pas être modifiée au moment de l'exécution. L'implémentation d'une sous-classe dépend si étroitement de sa classe parent que tout changement dans l'implémentation de la classe parent entraînera inévitablement des changements dans la sous-classe.

Lorsque vous devez réutiliser une sous-classe, si l'implémentation héritée n'est pas adaptée pour résoudre le nouveau problème, la classe parent doit la réécrire ou être remplacée par d'autres classes plus adaptées. Cette dépendance limite les propriétés de flexibilité et finalement limite. réutilisabilité. Comme alternative à l'héritage de fonctionnalités, la plupart adopteront le principe de réutilisation de composition/agrégation, « Principe de réutilisation de composition/agrégation » : utilisez la composition/agrégation autant que possible et essayez de ne pas utiliser l'héritage de classe.

L'utilisation des fonctionnalités d'héritage peut parfois être inappropriée si le nouveau type d'objet doit contenir des détails sur un comportement supplémentaire, par exemple lorsqu'il s'agit de types référence, de classes scellées ou d'interfaces. Face à ces exigences, nous écrivons parfois des classes statiques contenant des méthodes statiques. Mais trop de méthodes statiques entraîneront une surcharge inutile supplémentaire.

1. Aperçu des méthodes d'extension :

Face aux problèmes ci-dessus concernant "l'héritage" et face à certains besoins du projet, la façon dont nous devons résoudre ces problèmes est "les méthodes d'extension". ". Les « méthodes d'extension » ont été introduites dans C# 3.0, qui présentent non seulement les avantages des méthodes statiques, mais améliorent également la lisibilité du code qui les appelle. Lorsque vous utilisez des méthodes d'extension, vous pouvez appeler des méthodes statiques tout comme les méthodes d'instance.

1. Principes de base des méthodes d'extension :

(1) C# ne prend en charge que les méthodes d'extension et ne prend pas en charge les attributs étendus, les événements étendus, les opérateurs étendus, etc.

(2). Les méthodes d'extension (méthodes dont le premier paramètre est précédé de ceci) doivent être déclarées dans une classe statique non générique. La méthode d'extension doit avoir un paramètre, et seul le premier paramètre est marqué de ceci. .

(3). Lorsque le compilateur C# recherche des méthodes d'extension dans les classes statiques, il exige que ces classes statiques elles-mêmes aient une portée de fichier.

(4).La compilation C# nécessite des méthodes d'extension "d'importation". (Les méthodes statiques peuvent être nommées arbitrairement. Lorsque le compilateur C# recherche des méthodes, la recherche prend du temps. Il doit vérifier toutes les classes statiques dans la portée du fichier et analyser toutes leurs méthodes statiques pour trouver une correspondance)

(5). Plusieurs classes statiques peuvent définir la même méthode d'extension.

(6). Lorsque vous étendez un type avec une méthode d'extension, le type dérivé est également étendu.

2. Déclaration de méthode d'extension :

(1) Elle doit être dans une classe statique non imbriquée et non générique (il doit donc s'agir d'une méthode statique)

(2).Il y a au moins un paramètre.

(3). Le premier paramètre doit être préfixé par le mot-clé this.

(4). Le premier paramètre ne peut pas avoir d'autres modificateurs (tels que ref ou out).

(5). Le type du premier paramètre ne peut pas être un type pointeur.

Dans les deux descriptions de classification ci-dessus, une brève introduction est donnée aux caractéristiques de base et aux méthodes de déclaration des méthodes d'extension. L'utilisation des méthodes d'extension sera démontrée dans les exemples de code suivants. Encore une fois, sans autre explication.

2. Analyse du principe de la méthode d'extension :

La « méthode d'extension » est une méthode unique en C#, et l'attribut ExtensionAttribute est utilisé dans la méthode d'extension.

C# Une fois le premier paramètre d'une méthode statique marqué avec le mot-clé this, le compilateur appliquera en interne un attribut personnalisé à la méthode. Cet attribut sera inclus dans les métadonnées du fichier final généré pour les persistants. stockage, cette propriété se trouve dans l’assembly dll System.Core.

Tant qu'une classe statique contient au moins une méthode d'extension, cet attribut sera également appliqué à ses métadonnées. Tout assembly contenant au moins une classe statique répondant aux caractéristiques ci-dessus sera également appliqué à ses métadonnées. . Cet attribut. Si le code fait référence à une méthode d'instance qui n'existe pas, le compilateur analysera rapidement tous les assemblys référencés pour déterminer lesquels d'entre eux contiennent des méthodes d'extension. Ensuite, au sein de cet assembly, il pourra rechercher les classes statiques contenant des méthodes d'extension.

Si deux classes dans le même espace de noms contiennent des méthodes avec le même type d'extension, il n'y a aucun moyen d'utiliser uniquement la méthode d'extension dans l'une des classes. Pour utiliser un type par son nom simple (sans le préfixe de contrôle de nom), vous pouvez importer tous les espaces de noms dans lesquels le type existe, mais lorsque vous faites cela, vous n'avez aucun moyen d'empêcher également l'importation des méthodes d'extension dans cet espace de noms. .

Trois méthodes d'extension..NET3.5 énumérables et interrogeables :

Dans le framework, l'objectif principal des méthodes d'extension est de servir LINQ. Le framework fournit des méthodes d'extension auxiliaires, situées dans System. Classes énumérables et interrogeables sous l'espace de noms Linq. La plupart des extensions d'Enumerable sont IEnumerable8742468051c85b06f0a0af9e3e506b5c, et la plupart des extensions de Queryable sont IQueryable8742468051c85b06f0a0af9e3e506b5c.

1. Méthodes courantes dans la classe Enumerable

(1).Range() : Un paramètre est le numéro de départ et l'autre est le nombre de résultats à générer.

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


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn