owned this note
owned this note
Published
Linked with GitHub
---
tags: LinQ, LinQ基礎 , C#
---
# LINQ基礎 - Extension Method(擴充方法)
### [擴充方法](https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)
> Extension Method 在 .NET 3.5後開始提供
> .NET 3.5擴充方法可讓您在**現有類型中「加入」方法,而不需要建立新的衍生類型、重新編譯,或是修改原始類型**。擴充方法是一種特殊的靜態方法,但是會將它們當成擴充類型上的執行個體方法來呼叫。
> 擴充方法會定義為**靜態方法**,但**透過執行個體方法語法呼叫**。 擴充方法的**第一個參數會指定方法作業所在的類型**,而且**參數前面會加上 this 修飾詞**。 您**必須使用 using 指示詞將命名空間明確匯入至原始程式碼**,擴充方法才會進入範圍中。
> 擴充方法是定義在非巢狀且非泛型的靜態類別
由上面的引用 , 我們可知
1. 不需修改原來型別或重新編譯 , 就可以新增方法到現有型別上 , 並且可以使用.
2. 擴充方法必須是靜態類別中的靜態方法
3. 擴充方法的第一個參數代表此方法所要依附的型別
4. 擴充方法的第一個參數必須要在參數前面加上this 修飾詞.
5. 擴充方法不可以定義在巢狀型別或泛型型別中。
### 宣告&呼叫方式
1. 宣告自己的 namespace -> 對照 : ConsoleApp1
2. 宣告自己的 Static class -> 對照 : CityDateTimeExtension
3. 宣告自己的 static function -> 對照 : NextWeekDay
4. 第一個參數前要加 this , 參數型別代表要依附的型別 -> 對照 : DateTime
5. function 回傳型別 , 代表此擴充方法回傳的型別. (與一般函數同) -> 對照 : DateTime
以下新增一個擴充方法 NextWeekDay (回傳下一個星期的 DateTime ), 將其依附在 DateTime 類別. (原本 DateTime 類別沒有這個方法)
```
namespace ConsoleApp1
{
public static class CityDateTimeExtension
{
public static DateTime NextWeekDay(this DateTime from, DayOfWeek end)
{
int addDays = (int)end - (int)from.DayOfWeek;
return from.AddDays(addDays <= 0 ? addDays + 7 : addDays);
}
}
}
```
以下是測試程式碼 - 印出下一個禮拜四的日期時間.
在 **namespace ConsoleApp1 範圍內 , DateTime 可以呼叫NextWeekDay 這個方法.**
```
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now.NextWeekDay(DayOfWeek.Thursday));
Console.ReadKey();
}
}
}
```
---
### 呼叫方式種類
1. 靜態方法呼叫 : 擴充方法本來就是靜態方法 , 因此可以透過其型別的名稱去呼叫.
2. 擴充方法呼叫(物件) : 透過其依附型別的物件去呼叫
兩種呼叫方式 , 結果是相同的. 以下分別顯示兩種方法的呼叫方式.
##### 圖示說明
* namespace 為 ConsoleApp1
* 自定義一個 City 類別
* CityExtension 類別為靜態類別
* CityExtension 類別被定義在 ConsoleApp1 內
* 擴充方法 MaskName 依附在自定義類別 City 上
```C#
public class City
{
public string Name = "CityIsGoodPerson";
}
public static class CityExtension
{
public static int _plainTextLen = 6;
public static string _asterisk = "*";
public static string MaskName(this City city)
{
var orinialName = city.Name;
var result = orinialName;
if (orinialName.Length > _plainTextLen)
{
var plainText = orinialName.Substring(0, _plainTextLen);
var asteriskCount = orinialName.Length - _plainTextLen;
var encodingText = Enumerable.Repeat(_asterisk, asteriskCount).Aggregate((l, r) => l + r);
result = plainText + encodingText;
}
return result;
}
}
```
* 以下是呼叫程式碼
```C#
static void Main(string[] args)
{
var cityMan = new City();
Console.WriteLine("透過物件呼叫擴充方法結果 : " + cityMan.MaskName());
Console.WriteLine("靜態呼叫擴充方法結果 : " + CityExtension.MaskName(cityMan));
Console.ReadKey();
}
```
* 輸出結果
![tJ6hPOl.png](https://github.com/s0920832252/LinQ-Note/blob/master/Resources/tJ6hPOl.png?raw=true)
上述那個例子有兩點值得探討.
1. 擴充方法是屬於該靜態類別的 , 因此若是權限允許的話 , 自然也能存取靜態成員(實務上需要避免)
```C#
static void Main(string[] args)
{ // 測試程式碼
Console.WriteLine($"_plainTextLen = {CityExtension._plainTextLen}");
CityExtension._plainTextLen = 10;
Console.WriteLine($"_plainTextLen = {CityExtension._plainTextLen}");
Console.ReadKey();
}
```
輸出結果如下圖.
![AKfX1Vd.png](https://github.com/s0920832252/LinQ-Note/blob/master/Resources/AKfX1Vd.png?raw=true)
2. 即使是自定義類別 City , 也可以依附. 只要此擴充方法的靜態類別認識這個類別即可.
---
#### 結論
- 擴充方法的存在能夠讓程序員在不修改原本既有型別(包含介面)的前提下 , 就可以將新的方法內容附加到該型別之上.
- **擴充方法的使用範圍在其被定義的 namespace 中** , 因此只要 using 該 namespace , 也可以使用該擴充方法.
- 擴充方法無法修改不能內部資訊 , 所以不會破壞原有型別的封裝性. 固可以針對型別來設計方法 , 將方法封裝 , 讓原本的型別在使用上可以更加便利.
- **擴充方法不宜濫用** , 原因是會讓類別內的方法不一定被定義在該類別之中 , 此現象可能會造成**維護上的困難**.
- **擴充方法針對的是型別 !** , 而型別與物件導向的繼承有關 , 因此若是擴充方法依附的型別被其他子型別繼承或實現 , 則子型別也同樣可以使用該擴充方法.
- LinQ to Objects 的方法 , 其大部分都寫在 System.Linq(namespace) 裡面的Enumerable(static class) 中 , 且這些方法中大部分都依附在 IEnumerable<T> 這個**泛型介面**上. 也就是說只要 using System.Linq 這個命名空間 , 且繼承或實現 IEnumerable<T>這個介面的型別 , 都可以使用 Linq 的擴充方法. 例如 List<T> , IList<T> , Dictionary<TKey,TValue> 等等常見的泛型資料集合 , 都有實作 IEnumerable<T>. 另外有一些非泛型的資料集合 , 則通常都有實作 IEnumerable 這個**介面**(也就是可以使用 foreach )
---
### 補充
問題 : 假設現在某類別有一個方法A , 後來新增擴充方法A依附在該類別.
兩個方法的名稱 , 傳入參數型別 , 數量各方面都相同. 這樣是否會產生衝突或是編譯問題!?
回答 : 不會產生問題(編譯會過) , 但擴充方法不會被執行. 執行期間 , 系統會優先執行類別本身的方法.
---
### Thank you!
You can find me on
- [GitHub](https://github.com/s0920832252)
- [Facebook](https://www.facebook.com/fourtune.chen)
若有謬誤 , 煩請告知 , 新手發帖請多包涵
# :100: :muscle: :tada: :sheep: