從 C# 14 開始,最上層的非泛型 static class
宣告可以使用 extension
容器來宣告 擴充成員。 擴充成員是方法或屬性,可以顯示為實例或靜態成員。 舊版的 C# 會藉由將 做為修飾詞新增至最上層非泛型靜態類別中宣告之靜態方法的第一個參數,來啟用this
。
區塊 extension
會指定擴充成員的類型和接收者。 您可以在宣告內 extension
宣告方法和屬性。 下列範例會宣告定義實例擴充方法和實例屬性的單一擴充區塊。
public static class NumericSequences
{
extension(IEnumerable<int> sequence)
{
public IEnumerable<int> AddValue(int operand)
{
foreach (var item in sequence)
{
yield return item + operand;
}
}
public int Median
{
get
{
var sortedList = sequence.OrderBy(n => n).ToList();
int count = sortedList.Count;
int middleIndex = count / 2;
if (count % 2 == 0)
{
// Even number of elements: average the two middle elements
return (sortedList[middleIndex - 1] + sortedList[middleIndex]);
}
else
{
// Odd number of elements: return the middle element
return sortedList[middleIndex];
}
}
}
public int this[int index] => sequence.Skip(index).First();
}
}
extension
會定義接收者:sequence
,也就是 IEnumerable<int>
。 接收者類型可以是非泛型、開放式泛型或封閉式泛型類型。 此名稱 sequence
位於該延伸模組中宣告的每個實例成員範圍內。 擴充方法與 屬性都會存取 sequence
。
任何擴充成員都可以存取,就像是接收者類型的成員一樣:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
只要成員共用相同的接收者,就可以在單一容器中宣告任意數目的成員。 您也可以在單一類別中宣告多個擴充區塊。 不同的擴充功能不需要宣告相同的接收者類型或名稱。 如果唯一的成員是靜態的,擴充參數就不需要包含參數名稱:
extension(IEnumerable<int>)
{
// Method:
public static IEnumerable<int> Generate(int low, int count, int increment)
{
for (int i = 0; i < count; i++)
yield return low + (i * increment);
}
// Property:
public static IEnumerable<int> Identity => Enumerable.Empty<int>();
}
靜態延伸模組可以呼叫,就像是接收者類型的靜態成員:
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
這很重要
延伸模組不會導入成員宣告 的範圍 。 即使在多個延伸模組中,單一類別中宣告的所有成員都必須具有唯一的簽章。 產生的簽章會在其名稱中包含靜態成員的接收者類型,以及擴充實例成員的接收者參數。
下列範例示範使用 修飾詞的 this
擴充方法:
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
Add
方法可以從任何其他方法呼叫,就像它是介面的成員IEnumerable<int>
一樣:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
這兩種擴充方法都會產生相同的中繼語言(IL)。 來電者無法區分它們。 事實上,您可以將現有的擴充方法轉換成新的成員語法,而不需要重大變更。 格式同時與二進位和來源相容。
泛型擴充區塊
其中,您可以指定擴充區塊中宣告之擴充成員的類型參數,取決於需要該類型參數的位置:
- 當您在接收者中使用類型參數時,您會將 type 參數新增至
extension
宣告。 - 當類型與接收者上指定的任何類型參數不同時,您可以將類型參數新增至成員宣告。
- 您無法在這兩個位置中指定相同的類型參數。
下列範例顯示延伸模組區塊, IEnumerable<T>
其中兩個延伸模塊成員需要第二個類型參數:
public static class GenericExtensions
{
extension<TReceiver>(IEnumerable<TReceiver> source)
{
public IEnumerable<TReceiver> Spread(int start, int count)
=> source.Skip(start).Take(count);
public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach(TReceiver item in source)
{
yield return item;
}
foreach (TArg item in second)
{
yield return Converter(item);
}
}
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach (TArg item in second)
{
yield return Converter(item);
}
foreach (TReceiver item in source)
{
yield return item;
}
}
}
}
成員 Append
,並 Prepend
指定 轉換的額外 類型參數。 沒有任何成員會針對接收者重複類型參數。
對等的擴充方法宣告會示範這些型別參數的編碼方式:
public static class GenericExtensions
{
public static IEnumerable<T> Spread<T>(this IEnumerable<T> source, int start, int count)
=> source.Skip(start).Take(count);
public static IEnumerable<T1> Append<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T1 item in source)
{
yield return item;
}
foreach (T2 item in second)
{
yield return Converter(item);
}
}
public static IEnumerable<T1> Prepend<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T2 item in second)
{
yield return Converter(item);
}
foreach (T1 item in source)
{
yield return item;
}
}
}
另請參閱
C# 語言規格
如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法和使用方式的最終來源。