Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Ab C# 14 können nichtgenerische static class
-Deklarationen auf oberster Ebene extension
-Container verwenden, um Erweiterungsmember zu deklarieren. Erweiterungsmember sind Methoden oder Eigenschaften und können als Instanz- oder statische Mitglieder erscheinen. Frühere Versionen von C# ermöglichen Erweiterungsmethoden , indem sie dem ersten Parameter einer statischen Methode, die in einer nichtgenerischen statischen Klasse auf oberster Ebene deklariert ist, als Modifizierer hinzufügen this
.
Der extension
Block spezifiziert den Typ und den Empfänger für Erweiterungsmitglieder. Sie können Methoden und Eigenschaften innerhalb der extension
Deklaration deklarieren. Im folgenden Beispiel wird ein einzelner Erweiterungsblock deklariert, der eine Instanzerweiterungsmethode und eine Instanzeigenschaft definiert.
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();
}
}
Der extension
definiert den Empfänger: sequence
, welches ein IEnumerable<int>
ist. Der Empfängertyp kann nichtgenerisch, ein offenes Generika oder ein geschlossener generischer Typ sein. Der Name sequence
ist im Gültigkeitsbereich jedes Instanzmembers, der in dieser Erweiterung deklariert ist. Die Erweiterungsmethode und die Eigenschaft greifen beide auf sequence
zu.
Auf alle Erweiterungsmitglieder kann zugegriffen werden, als wären sie Mitglieder des Empfängertyps:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
Sie können eine beliebige Anzahl von Mitgliedern in einem einzelnen Container deklarieren, solange sie denselben Empfänger gemeinsam nutzen. Sie können auch so viele Erweiterungsblöcke in einer einzelnen Klasse deklarieren. Unterschiedliche Erweiterungen müssen nicht denselben Typ oder denselben Empfängernamen deklarieren. Der Erweiterungsparameter muss den Parameternamen nicht enthalten, wenn die einzigen Member statisch sind:
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>();
}
Statische Erweiterungen können aufgerufen werden, als wären sie statische Member des Empfängertyps:
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
Von Bedeutung
Eine Erweiterung führt keinen Gültigkeitsbereich für Memberdeklarationen ein. Alle in einer einzigen Klasse deklarierten Member müssen eindeutige Signaturen aufweisen, selbst wenn sie über mehrere Erweiterungen verteilt sind. Die generierte Signatur enthält den Empfängertyp in ihrem Namen für statische Member und den Empfängerparameter für Erweiterungsinstanzmember.
Das folgende Beispiel zeigt eine Erweiterungsmethode mit dem this
Modifizierer:
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
Die Add
Methode kann von jeder anderen Methode aufgerufen werden, als ob es sich um ein Element der IEnumerable<int>
Schnittstelle handelte:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
Beide Formen von Erweiterungsmethoden generieren dieselbe Zwischensprache (IL). Anrufer können keinen Unterschied zwischen ihnen machen. Tatsächlich können Sie vorhandene Erweiterungsmethoden in die neue Membersyntax umwandeln, ohne einen Bruch zu verursachen. Die Formate sind sowohl binär als auch quellkompatibel.
Generische Erweiterungsblöcke
Wo Sie die Typparameter für ein erweiterungsmitglied angeben, das in einem Erweiterungsblock deklariert ist, hängt davon ab, wo dieser Typparameter erforderlich ist:
- Sie fügen der Deklaration den Typparameter
extension
hinzu, wenn der Typparameter im Empfänger verwendet wird. - Sie fügen der Memberdeklaration den Typparameter hinzu, wenn sich der Typ von jedem Typparameter unterscheidet, der für den Empfänger angegeben ist.
- Sie können in beiden Speicherorten nicht denselben Typparameter angeben.
Das folgende Beispiel zeigt einen Erweiterungsblock, für IEnumerable<T>
den zwei der Erweiterungsmember einen zweiten Typparameter erfordern:
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;
}
}
}
}
Die Member Append
und Prepend
geben den zusätzlichen Typparameter für die Konvertierung an. Keines der Mitglieder wiederholt den Typparameter für den Empfänger.
Die entsprechenden Erweiterungsmethodedeklarationen veranschaulichen, wie diese Typparameter codiert werden:
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;
}
}
}
Siehe auch
C#-Sprachspezifikation
Weitere Informationen erhalten Sie unter C#-Sprachspezifikation. Die Sprachspezifikation ist die endgültige Quelle für C#-Syntax und -Verwendung.