>  기사  >  백엔드 개발  >  C# 확장 방법 분석

C# 확장 방법 분석

黄舟
黄舟원래의
2017-02-07 15:05:181117검색

객체 지향 언어를 사용하는 프로젝트 개발 과정에서 '상속' 기능이 자주 사용되지만 모든 시나리오가 '상속' 기능을 사용하는 데 적합한 것은 아닙니다. 또한 디자인 패턴의 몇 가지 기본 원칙이 더 언급됩니다. .

상속 관련 기능 사용으로 인해 발생하는 문제: 객체의 상속 관계는 실제로 컴파일 타임에 정의되므로 상위 클래스에서 상속된 구현을 런타임에 변경할 수 없습니다. 하위 클래스의 구현은 상위 클래스에 너무 밀접하게 의존하므로 상위 클래스 구현이 변경되면 필연적으로 하위 클래스도 변경됩니다.

하위 클래스를 재사용해야 할 때 상속된 구현이 새 문제를 해결하는 데 적합하지 않으면 상위 클래스를 다시 작성하거나 더 적합한 다른 클래스로 대체해야 합니다. 이러한 종속성은 유연성 속성을 제한하고 궁극적으로 제한됩니다. 재사용성. 기능 상속의 대안으로 대부분은 구성/집계 재사용 원칙, "구성/집계 재사용 원칙"을 채택합니다. 가능한 한 구성/집계를 사용하고 클래스 상속은 사용하지 않으려고 합니다.

예를 들어 참조 유형, 봉인 클래스 또는 인터페이스를 처리할 때와 같이 새로운 유형의 객체가 추가 동작에 대한 세부 정보를 전달해야 하는 경우 상속 기능을 사용하는 것이 때때로 부적절할 수 있습니다. 이러한 요구 사항에 직면할 때 우리는 때때로 일부 정적 메서드를 포함하는 일부 정적 클래스를 작성합니다. 그러나 정적 메서드가 너무 많으면 불필요한 추가 오버헤드가 발생합니다.

1. 확장 방법 개요:

위의 "상속" 문제에 직면하고 프로젝트의 일부 요구 사항에 직면했을 때 이러한 문제를 해결하는 방법은 "확장 방법"입니다. ". "확장 메서드"는 C# 3.0에 도입되었는데, 이는 정적 메서드의 장점을 가질 뿐만 아니라 이를 호출하는 코드의 가독성도 향상시킵니다. 확장 메서드를 사용하면 인스턴스 메서드와 마찬가지로 정적 메서드를 호출할 수 있습니다.

1. 확장 메서드의 기본 원칙:

(1) C#은 확장 메서드만 지원하고 확장 속성, 확장 이벤트, 확장 연산자 등은 지원하지 않습니다.

(2) 확장 메서드(첫 번째 매개변수가 이 뒤에 오는 메서드)는 제네릭이 아닌 정적 클래스에서 선언되어야 하며 첫 번째 매개변수만 이 표시로 표시됩니다. .

(3) C# 컴파일러가 정적 클래스에서 확장 메서드를 찾을 때 이러한 정적 클래스 자체에 파일 범위가 있어야 합니다.

(4).C# 컴파일에는 확장 메서드 "가져오기"가 필요합니다. (정적 메서드의 이름은 임의로 지정할 수 있습니다. C# 컴파일러가 메서드를 찾을 때 검색하는 데 시간이 걸립니다. 파일 범위의 모든 정적 클래스를 확인하고 일치하는 항목을 찾기 위해 모든 정적 메서드를 검색해야 합니다.)

(5) 여러 정적 클래스가 동일한 확장 메서드를 정의할 수 있습니다.

(6) 확장 메서드를 사용하여 유형을 확장하면 파생 유형도 확장됩니다.

2. 확장 메서드 선언:

(1) 중첩되지 않고 제네릭이 아닌 정적 클래스에 있어야 합니다(따라서 정적 메서드여야 함)

(2).하나 이상의 매개변수가 있습니다.

(3) 첫 번째 매개변수 앞에는 this 키워드가 와야 합니다.

(4) 첫 번째 매개변수는 다른 수정자(예: ref 또는 out)를 가질 수 없습니다.

(5) 첫 번째 매개변수의 유형은 포인터 유형이 될 수 없습니다.

위의 두 가지 분류 설명은 확장 메서드의 기본 특성과 선언 방법에 대해 간략하게 소개합니다. 다음 코드 샘플에서는 다시 설명하지 않습니다.

2. 확장 메서드의 원리 분석:

"확장 메서드"는 C# 고유의 메서드로 ExtensionAttribute 속성이 확장 메서드에 사용됩니다.

C# 정적 메서드의 첫 번째 매개 변수가 this 키워드로 표시되면 컴파일러는 내부적으로 사용자 정의된 속성을 메서드에 적용합니다. 이 속성은 최종 생성된 파일의 메타데이터에 포함됩니다. 저장소의 경우 이 속성은 System.Core dll 어셈블리에 있습니다.

정적 클래스에 확장 메서드가 하나 이상 포함되어 있는 한 이 특성은 해당 메타데이터에도 적용됩니다. 위 특성을 충족하는 정적 클래스가 하나 이상 포함된 모든 어셈블리도 해당 메타데이터에 적용됩니다. . 이 속성은 . 코드가 존재하지 않는 인스턴스 메서드를 참조하는 경우 컴파일러는 참조된 모든 어셈블리를 신속하게 검색하여 확장 메서드가 포함된 어셈블리를 확인한 다음 이 어셈블리 내에서 확장 메서드가 포함된 정적 클래스를 검색할 수 있습니다.

동일한 네임스페이스에 있는 두 클래스에 동일한 확장 유형의 메서드가 포함되어 있는 경우 두 클래스 중 하나의 확장 메서드만 사용할 수 있는 방법은 없습니다. 이름 지정 제어 접두사 없이 간단한 이름으로 형식을 사용하려면 해당 형식이 존재하는 모든 네임스페이스를 가져올 수 있지만 이렇게 하면 해당 네임스페이스의 확장 메서드도 가져오는 것을 방지할 수 없습니다. .

Enumerable 및 Queryable 세 가지..NET3.5 확장 메서드:

프레임워크에서 확장 메서드의 가장 큰 목적은 LINQ를 제공하는 것입니다. 프레임워크는 System에 있는 보조 확장 메서드를 제공합니다. Linq 네임스페이스 아래의 열거 가능 및 쿼리 가능 클래스입니다. Enumerable의 대부분의 확장은 IEnumerable이고, Queryable의 대부분의 확장은 IQueryable입니다.

1. Enumerable 클래스의 일반적인 메서드

(1).Range(): 한 매개 변수는 시작 번호이고 다른 매개 변수는 생성할 결과 수입니다.

아아아아


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


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.