首頁 後端開發 C#.Net教程 C#的擴充方法解析

C#的擴充方法解析

Feb 07, 2017 pm 03:05 PM
c#

在使用物件導向的語言進行專案開發的過程中,較多的會使用到「繼承」的特性,但是並非所有的場景都適合使用「繼承」特性,在設計模式的一些基本原則中也有較多的提到。

繼承的有關特性的使用所帶來的問題:物件的繼承關係實在編譯時就定義好了,所以無法在執行時間改變從父類別繼承的實作。子類別的實作與它父類別有非常緊密的依賴關係,以至於父類別實作中的任何變更必然會導致子類別發生變化。

當你需要復用子類別時,如果繼承下來的實作不適合解決新的問題,則父類別必須重寫它或被其他更適合的類別替換,這種依賴關係限制了靈活性並最終限制了復用性。取代繼承特性的方式,較多的會採用 合成/聚合復用原則,「合成/聚合復用原則」:盡量使用合成/聚合,盡量不要使用類別繼承。

如果在新類型的物件應當攜帶有關額外行為的細節,在使用繼承特性時,有時可能不太適合,例如:處理指類型,密封類,或介面時。在面對這些要求時,我們有時會寫一些靜態類別包含一些靜態方法。但是過多的靜態方法會造成額外的不必要的開銷。

一.擴展方法概述:

面對以上的有關「繼承」的問題,以及在面對專案的一些需求時,我們需要解決這些問題的方式就是「擴展方法」。在C#3.0中引入了“擴展方法”,既有靜態方法的優點,又使調用它們的程式碼的可讀性得到了提高。在使用擴充方法時,可以像呼叫實例方法一樣呼叫靜態方法。

1.擴充方法的基本原則:

(1).C#只支援擴充方法,不支援擴充屬性、擴充事件、擴充操作符等。

(2).擴充方法(第一個參數前面是this的方法)必須在非泛型的靜態類別中聲明,擴充方法必須有一個參數,而且只有第一個參數使用this標記。

(3).C#編譯器尋找靜態類別中的擴充方法時,要求這些靜態類別本身必須具有檔案作用域。

(4).C#編譯要求「導入」擴充方法。 (靜態方法可以任意命名,C#編譯器在尋找方法時,需要花費時間進行查找,需要檢查文件作用域中的所有的靜態類,並掃描它們的所有靜態方法來查找一個匹配)

(5) .多個靜態類別可以定義相同的擴充方法。

(6).用一個擴充方法擴充一個型別時,同時也擴充了衍生型別。 

2.擴充方法宣告:

(1).必須在一個非嵌套的、非泛型型的靜態類別中(所以必須是一個靜態方法)

(2).至少有一個參數。

(3).第一個參數必須附加this關鍵字做前綴。

(4).第一個參數不能有其他任何修飾符(如ref或out)。

(5).第一個參數的型別不能是指標型別。

以上的兩個分類說明中,對擴展方法的基本特性和聲明方式做了一個簡單的介紹,有關擴展方法的使用方式,會在後面的代碼樣例中進行展示,再次就不再多做說明。

二.擴充方法原理解析:

「擴充方法」是C#獨有的一種方法,在擴充方法中會使用ExtensionAttribute這個attribute。

C#一旦使用this關鍵字標記了某個靜態方法的第一個參數,編譯器就會在內部向該方法應用一個定制的attribute,這個attribute會在最終生成的文件的元數據中持久性的儲存下來,此屬性在System.Core dll程式集中。

任何靜態類別只要包含了至少一個擴充方法,它的元資料中也會應用這個attribute,任何一個組件包含了至少一個符合上述特點的靜態類,它的元資料也會應用這個attribute。如果程式碼嗲用了一個不存在的實例方法,編譯器會快速的掃描引用的所有程序集,判斷它們哪些包含了擴展方法,然後,在這個程式集中,可以掃描包含了擴展方法的靜態類別。

如果同一個命名空間中的兩個類別含有擴展類型相同的方法,就沒有辦法做到只用其中一個類別中的擴充方法。為了透過類型的簡單名稱(沒有命名控制項前綴)來使用類型,可以匯入該類型所有在的命名空間,但這樣做的時候,你沒有辦法阻止那個命名空間中的擴充方法也被匯入進來。

三..NET3.5的擴展方法Enumerable和Queryable:

在框架中,擴展方法最大的用途就是為LINQ服務,框架提供了輔助的擴展方法,位於System.Linq命名空間下的Enumerable和Queryable類。 Enumerable大多數擴充是IEnumerable,Queryable大大多數擴充是IQueryable

1.Enumerable類別中的常用方法

(1).Range():一個參數是起始數,一個是要產生的結果數。

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


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
使用 C# 的活動目錄 使用 C# 的活動目錄 Sep 03, 2024 pm 03:33 PM

使用 C# 的 Active Directory 指南。在這裡,我們討論 Active Directory 在 C# 中的介紹和工作原理以及語法和範例。

C# 中的隨機數產生器 C# 中的隨機數產生器 Sep 03, 2024 pm 03:34 PM

C# 隨機數產生器指南。在這裡,我們討論隨機數產生器的工作原理、偽隨機數和安全數的概念。

C# 資料網格視圖 C# 資料網格視圖 Sep 03, 2024 pm 03:32 PM

C# 資料網格視圖指南。在這裡,我們討論如何從 SQL 資料庫或 Excel 檔案載入和匯出資料網格視圖的範例。

C# 中的階乘 C# 中的階乘 Sep 03, 2024 pm 03:34 PM

C# 階乘指南。這裡我們討論 C# 中階乘的介紹以及不同的範例和程式碼實作。

c#多線程和異步的區別 c#多線程和異步的區別 Apr 03, 2025 pm 02:57 PM

多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

C# 中的模式 C# 中的模式 Sep 03, 2024 pm 03:33 PM

C# 模式指南。在這裡,我們討論 C# 中模式的介紹和前 3 種類型,以及其範例和程式碼實作。

C# 中的質數 C# 中的質數 Sep 03, 2024 pm 03:35 PM

C# 質數指南。這裡我們討論c#中素數的介紹和範例以及程式碼實作。

xml怎麼改格式 xml怎麼改格式 Apr 03, 2025 am 08:42 AM

可以採用多種方法修改 XML 格式:使用文本編輯器(如 Notepad )進行手工編輯;使用在線或桌面 XML 格式化工具(如 XMLbeautifier)進行自動格式化;使用 XML 轉換工具(如 XSLT)定義轉換規則;或者使用編程語言(如 Python)進行解析和操作。修改時需謹慎,並備份原始文件。

See all articles