Statische abstrakte Member in Schnittstellen
Anmerkung
Dieser Artikel ist eine Featurespezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.
Es kann einige Abweichungen zwischen der Featurespezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den entsprechenden Hinweisen zum Language Design Meeting (LDM) erfasst.
Weitere Informationen zum Prozess für die Aufnahme von Funktions-Speclets in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.
Zusammenfassung
Eine Schnittstelle darf abstrakte statische Member angeben, für die anschließend implementierende Klassen und Strukturen eine explizite oder implizite Implementierung bereitstellen müssen. Auf die Member kann über Typparameter zugegriffen werden, die durch die Schnittstelle eingeschränkt werden.
Motivation
Es gibt derzeit keine Möglichkeit, über statische Member hinweg zu abstrahieren und generalisierten Code zu schreiben, der für alle Typen gilt, die diese statischen Member definieren. Dies ist besonders problematisch für Memberarten, die nur in statischer Form vorhanden sind, insbesondere Operatoren.
Dieses Feature ermöglicht generische Algorithmen über numerische Typen, dargestellt durch Schnittstelleneinschränkungen, die das Vorhandensein bestimmter Operatoren angeben. Die Algorithmen können daher in Bezug auf solche Operatoren ausgedrückt werden:
// Interface specifies static properties and operators
interface IAddable<T> where T : IAddable<T>
{
static abstract T Zero { get; }
static abstract T operator +(T t1, T t2);
}
// Classes and structs (including built-ins) can implement interface
struct Int32 : …, IAddable<Int32>
{
static Int32 IAddable.operator +(Int32 x, Int32 y) => x + y; // Explicit
public static int Zero => 0; // Implicit
}
// Generic algorithms can use static members on T
public static T AddAll<T>(T[] ts) where T : IAddable<T>
{
T result = T.Zero; // Call static operator
foreach (T t in ts) { result += t; } // Use `+`
return result;
}
// Generic method can be applied to built-in and user-defined types
int sixtyThree = AddAll(new [] { 1, 2, 4, 8, 16, 32 });
Syntax
Schnittstellenmitglieder
Das Feature würde es ermöglichen, statische Schnittstellenmitglieder als virtuell zu deklarieren.
Regeln vor C# 11
Vor C# 11 sind Instanzmember in Schnittstellen implizit abstrakt (oder virtuell, wenn es eine Standardimplementierung gibt), können jedoch optional einen abstract
-Modifizierer (oder virtual
-Modifizierer) aufweisen. Nicht-virtuelle Instanzmember müssen explizit als sealed
gekennzeichnet werden.
Statische Schnittstellenmember sind derzeit implizit nicht-virtuell und lassen keine abstract
-, virtual
- oder sealed
-Modifizierer zu.
Vorschlag
Abstrakte statische Elemente
Andere statische Schnittstellenelemente als Felder dürfen auch den abstract
-Modifizierer verwenden. Abstrakte statische Member dürfen keinen Körper haben (im Fall von Eigenschaften dürfen die Accessoren keinen Körper haben).
interface I<T> where T : I<T>
{
static abstract void M();
static abstract T P { get; set; }
static abstract event Action E;
static abstract T operator +(T l, T r);
static abstract bool operator ==(T l, T r);
static abstract bool operator !=(T l, T r);
static abstract implicit operator T(string s);
static abstract explicit operator string(T t);
}
Virtuelle statische Member
Statische Schnittstellenelemente außer Feldern dürfen ebenfalls den virtual
-Modifizierer haben. Virtuelle statische Member müssen einen Körper haben.
interface I<T> where T : I<T>
{
static virtual void M() {}
static virtual T P { get; set; }
static virtual event Action E;
static virtual T operator +(T l, T r) { throw new NotImplementedException(); }
}
Explizit nicht-virtuelle statische Member
Zur Wahrung der Symmetrie mit nicht-virtuellen Instanzmembern sollten statische Member (mit Ausnahme von Feldern) einen optionalen sealed
-Modifizierer zulassen, auch wenn sie standardmäßig nicht-virtuell sind:
interface I0
{
static sealed void M() => Console.WriteLine("Default behavior");
static sealed int f = 0;
static sealed int P1 { get; set; }
static sealed int P2 { get => f; set => f = value; }
static sealed event Action E1;
static sealed event Action E2 { add => E1 += value; remove => E1 -= value; }
static sealed I0 operator +(I0 l, I0 r) => l;
}
Implementierung von Schnittstellenmembern
Aktuelle Regeln
Klassen und Strukturen können abstrakte Instanzmitglieder von Schnittstellen entweder implizit oder explizit implementieren. Ein implizit implementiertes Schnittstellenmember ist eine normale (virtuelle oder nicht-virtuelle) Memberdeklaration der Klasse oder Struktur, die „zufällig“ auch das Schnittstellenmember implementiert. Das Member kann sogar von einer Basisklasse geerbt werden und muss daher in der Klassendeklaration noch nicht einmal vorhanden sein.
Ein explizit implementiertes Schnittstellenmitglied verwendet einen qualifizierten Namen, um das betreffende Schnittstellenelement zu identifizieren. Auf die Implementierung kann nicht direkt als Mitglied der Klasse oder Struktur zugegriffen werden, sondern nur über die Schnittstelle.
Vorschlag
Es ist keine neue Syntax in Klassen und Strukturen erforderlich, um die implizite Implementierung statischer abstrakter Schnittstellenmitglieder zu erleichtern. Vorhandene statische Memberdeklarationen dienen diesem Zweck.
Explizite Implementierungen statischer abstrakter Schnittstellenmember verwenden einen qualifizierten Namen zusammen mit dem static
-Modifizierer.
class C : I<C>
{
string _s;
public C(string s) => _s = s;
static void I<C>.M() => Console.WriteLine("Implementation");
static C I<C>.P { get; set; }
static event Action I<C>.E // event declaration must use field accessor syntax
{
add { ... }
remove { ... }
}
static C I<C>.operator +(C l, C r) => new C($"{l._s} {r._s}");
static bool I<C>.operator ==(C l, C r) => l._s == r._s;
static bool I<C>.operator !=(C l, C r) => l._s != r._s;
static implicit I<C>.operator C(string s) => new C(s);
static explicit I<C>.operator string(C c) => c._s;
}
Semantik
Einschränkungen für Operatoren
Aktuell müssen alle unären und binären Operatordeklarationen die Anforderung erfüllen, dass mindestens ein Operand den Typ T
oder T?
hat, wobei T
der Instanztyp des einschließenden Typs ist.
Diese Anforderungen müssen gelockert werden, damit ein eingeschränkter Operand einen Typparameter haben kann, der als „Instanztyp des einschließenden Typs“ zählt.
Damit ein Typparameter T
als „Instanztyp des einschließenden Typs“ zählen kann, muss er die folgenden Anforderungen erfüllen:
-
T
ist ein direkter Typparameter in der Schnittstelle, in der die Operatordeklaration enthalten ist, und -
T
ist direkt durch den „Instanztyp“ wie in der Spezifikation genannt eingeschränkt, d. h. die umgebende Schnittstelle mit ihren eigenen Typparametern, die als Typargumente verwendet werden.
Gleichheitsoperatoren und Umwandlungen
Abstrakte/virtuelle Deklarationen von ==
- und !=
Operatoren sowie abstrakte/virtuelle Deklarationen impliziter und expliziter Konvertierungsoperatoren sind in Schnittstellen zulässig. Abgeleitete Schnittstellen werden ebenfalls in der Lage sein, sie zu implementieren.
Für die Operatoren ==
und !=
muss mindestens ein Parametertyp ein Typparameter sein, der als „Instanztyp des einschließenden Typs“ zählt, wie im vorherigen Abschnitt definiert.
Implementieren statischer abstrakter Member
Die Regeln dafür, wann eine statische Memberdeklaration in einer Klasse oder Struktur als Implementierung eines statischen abstrakten Schnittstellenmembers betrachtet wird, und die entsprechenden Anforderungen sind mit den Regeln und Anforderungen von Instanzmembern identisch.
TBD: Hier sind möglicherweise zusätzliche oder unterschiedliche Regeln erforderlich, an die wir noch nicht gedacht haben.
Schnittstellen als Typargumente
Wir haben das von https://github.com/dotnet/csharplang/issues/5955 angesprochene Problem erörtert und beschlossen, eine Einschränkung für die Verwendung einer Schnittstelle als Typargument (https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-28.md#type-hole-in-static-abstracts) hinzuzufügen. Hier ist die Einschränkung, wie sie von https://github.com/dotnet/csharplang/issues/5955 vorgeschlagen und von der LDM genehmigt wurde.
Eine Schnittstelle, die ein statisches abstraktes/virtuelles Member enthält oder erbt und nicht die jeweils spezifischste Implementierung in der Schnittstelle besitzt, kann nicht als Typargument verwendet werden. Wenn alle statischen abstrakten/virtuellen Member die jeweils spezifischste Implementierung besitzen, kann die Schnittstelle als Typargument verwendet werden.
Zugreifen auf statische abstrakte Schnittstellenmitglieder
Auf ein statisches abstraktes Schnittstellenmember M
kann in einem Typparameter T
mithilfe des Ausdrucks T.M
zugegriffen werden, wenn T
durch eine Schnittstelle I
eingeschränkt wird und M
ein zugängliches statisches abstraktes Member von I
ist.
T M<T>() where T : I<T>
{
T.M();
T t = T.P;
T.E += () => { };
return t + T.P;
}
Zur Laufzeit ist die tatsächlich verwendete Memberimplementierung diejenige, die im tatsächlichen Typ vorhanden ist, der als Typargument angegeben ist.
C c = M<C>(); // The static members of C get called
Da Abfrageausdrücke als syntaktische Umschreibung spezifiziert sind, können Sie in C# einen Typ als Abfragequelle verwenden, solange er statische Member für die von Ihnen verwendeten Abfrageoperatoren bereitstellt. Anders ausgedrückt: Wenn die Syntax passt, lassen wir dies zu. Wir glauben, dass dieses Verhalten in der ursprünglichen Version von LINQ nicht beabsichtigt oder wichtig war, und wir möchten den Aufwand nicht betreiben, es für Typparameter zu unterstützen. Wenn es Szenarien gibt, werden wir von ihnen erfahren und können entscheiden, ob wir sie später behandeln.
Varianzsicherheit §18.2.3.2
Varianzsicherheitsregeln sollten auf Signaturen statischer abstrakter Elemente angewendet werden. Die in https://github.com/dotnet/csharplang/blob/main/proposals/variance-safety-for-static-interface-members.md#variance-safety vorgeschlagene Addition sollte geändert werden:
Diese Einschränkungen gelten nicht für Typen innerhalb von Deklarationen statischer Member.
Bis
Diese Einschränkungen gelten nicht für Typen in Deklarationen von nicht-virtuellen, nicht-abstrakten statischen Membern.
§10.5.4 Benutzerdefinierte implizite Konvertierungen
Die folgenden Aufzählungspunkte
- Bestimmen Sie die Typen
S
,S₀
undT₀
.- Wenn
E
einen Typ aufweist, lassen SieS
diesen Typ sein. - Wenn
S
oderT
Nullwerttypen sind, solltenSᵢ
undTᵢ
die zugrunde liegenden Typen sein. Andernfalls solltenSᵢ
undTᵢ
S
bzw.T
sein. - Wenn
Sᵢ
oderTᵢ
Typparameter sind, sollenS₀
undT₀
als ihre effektiven Basisklassen fungieren, andernfalls sollenS₀
undT₀
entsprechendSₓ
undTᵢ
sein.
- Wenn
- Suchen Sie die Gruppe von Typen,
D
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS0
(wennS0
eine Klasse oder Struktur ist), den Basisklassen vonS0
(wennS0
eine Klasse ist) undT0
(wennT0
eine Klasse oder Struktur ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U
. Dieser Satz besteht aus den benutzerdefinierten und gelifteten impliziten Umwandlungsoperatoren, die von den Klassen oder Strukturen inD
deklariert werden und aus einem Typ, derS
einschließt, in einen Typ, derT
einschließt, umgewandelt werden. WennU
leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.
werden wie folgt geändert:
- Bestimmen Sie die Typen
S
,S₀
undT₀
.- Wenn
E
einen Typ aufweist, lassen SieS
diesen Typ sein. - Wenn
S
oderT
Nullwerttypen sind, solltenSᵢ
undTᵢ
die zugrunde liegenden Typen sein. Andernfalls solltenSᵢ
undTᵢ
S
bzw.T
sein. - Wenn
Sᵢ
oderTᵢ
Typparameter sind, dann sindS₀
undT₀
ihre effektiven Basisklassen; andernfalls sindS₀
undT₀
Sₓ
bzw.Tᵢ
.
- Wenn
- Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U
.- Suchen Sie die Gruppe von Typen,
D1
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS0
(wennS0
eine Klasse oder Struktur ist), den Basisklassen vonS0
(wennS0
eine Klasse ist) undT0
(wennT0
eine Klasse oder Struktur ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U1
. Dieser Satz besteht aus den benutzerdefinierten und gelifteten impliziten Umwandlungsoperatoren, die von den Klassen oder Strukturen inD1
deklariert werden und aus einem Typ, derS
einschließt, in einen Typ, derT
einschließt, umgewandelt werden. - Wenn
U1
nicht leer ist, istU
U1
. Andernfalls,- Suchen Sie die Gruppe von Typen,
D2
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Dieser Satz besteht ausSᵢ
effektiver Schnittstellensatz und deren Basisschnittstellen (wennSᵢ
ein Typparameter ist) undTᵢ
effektiver Schnittstellensatz (wennTᵢ
ein Typparameter ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U2
. Dieser Satz besteht aus den benutzerdefinierten und gelifteten impliziten Umwandlungsoperatoren, die von den Schnittstellen inD2
deklariert werden und aus einem Typ, derS
einschließt, in einen Typ, derT
einschließt, umgewandelt werden. - Wenn
U2
nicht leer ist, istU
U2
- Suchen Sie die Gruppe von Typen,
- Suchen Sie die Gruppe von Typen,
- Wenn
U
leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.
§10.3.9 Benutzerdefinierte explizite Umwandlungen
Die folgenden Aufzählungspunkte
- Bestimmen Sie die Typen
S
,S₀
undT₀
.- Wenn
E
einen Typ aufweist, lassen SieS
diesen Typ sein. - Wenn
S
oderT
Nullwerttypen sind, solltenSᵢ
undTᵢ
die zugrunde liegenden Typen sein. Andernfalls solltenSᵢ
undTᵢ
S
bzw.T
sein. - Wenn
Sᵢ
oderTᵢ
Typparameter sind, solltenS₀
undT₀
ihre effektiven Basisklassen sein. Andernfalls solltenS₀
undT₀
Sᵢ
bzw.Tᵢ
sein.
- Wenn
- Suchen Sie die Gruppe von Typen,
D
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS0
(wennS0
eine Klasse oder Struktur ist), die BasisklassenS0
(wennS0
eine Klasse ist),T0
(wennT0
eine Klasse oder Struktur ist) und die Basisklassen vonT0
(wennT0
eine Klasse ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U
. Diese Gruppe besteht aus den benutzerdefinierten und gelifteten impliziten oder expliziten Umwandlungsoperatoren, die von den Klassen oder Strukturen inD
deklariert werden und aus einem Typ, derS
einschließt oder hiervon eingeschlossen wird, in einen Typ umgewandelt werden, derT
einschließt oder hiervon eingeschlossen wird. WennU
leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.
werden wie folgt geändert:
- Bestimmen Sie die Typen
S
,S₀
undT₀
.- Wenn
E
einen Typ aufweist, lassen SieS
diesen Typ sein. - Wenn
S
oderT
Nullwerttypen sind, solltenSᵢ
undTᵢ
die zugrunde liegenden Typen sein. Andernfalls solltenSᵢ
undTᵢ
S
bzw.T
sein. - Wenn
Sᵢ
oderTᵢ
Typparameter sind, sollenS₀
undT₀
ihre effektiven Basisklassen sein; andernfalls sollenS₀
undT₀
Sᵢ
bzw.Tᵢ
sein.
- Wenn
- Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U
.- Suchen Sie die Gruppe von Typen,
D1
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS0
(wennS0
eine Klasse oder Struktur ist), die BasisklassenS0
(wennS0
eine Klasse ist),T0
(wennT0
eine Klasse oder Struktur ist) und die Basisklassen vonT0
(wennT0
eine Klasse ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U1
. Diese Gruppe besteht aus den benutzerdefinierten und gelifteten impliziten oder expliziten Umwandlungsoperatoren, die von den Klassen oder Strukturen inD1
deklariert werden und aus einem Typ, derS
einschließt oder hiervon eingeschlossen wird, in einen Typ umgewandelt werden, derT
einschließt oder hiervon eingeschlossen wird. - Wenn
U1
nicht leer ist, istU
U1
. Andernfalls,- Suchen Sie die Gruppe von Typen,
D2
, aus der benutzerdefinierte Umwandlungsoperatoren berücksichtigt werden. Diese Gruppe besteht ausSᵢ
effektiven Schnittstellensätzen und ihren Basisschnittstellen (wennSᵢ
ein Typparameter ist) sowie ausTᵢ
effektiven Schnittstellensätzen und ihren Basisschnittstellen (wennTᵢ
ein Typparameter ist). - Ermitteln Sie die Gruppe anwendbarer benutzerdefinierter und gelifteter Umwandlungsoperatoren,
U2
. Diese Gruppe besteht aus den benutzerdefinierten und gelifteten impliziten oder expliziten Umwandlungsoperatoren, die von den Schnittstellen inD2
deklariert werden und aus einem Typ, derS
einschließt oder hiervon eingeschlossen wird, in einen Typ umgewandelt werden, derT
einschließt oder hiervon eingeschlossen wird. - Wenn
U2
nicht leer ist, istU
U2
- Suchen Sie die Gruppe von Typen,
- Suchen Sie die Gruppe von Typen,
- Wenn
U
leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.
Standardimplementierungen
Ein zusätzliches Merkmal dieses Vorschlags besteht darin, dass statischen virtuellen Membern in Schnittstellen Standardimplementierungen ermöglicht werden, wie dies bereits bei virtuellen/abstrakten Instanzmembern der Fall ist.
Eine Komplikation hier ist, dass Standardimplementierungen andere statische virtuelle Member "virtuell" aufrufen möchten. Der direkte Aufruf von statischen virtuellen Membern auf der Schnittstelle würde die Übermittlung eines verborgenen Typparameters erfordern, der den „self“-Typ darstellt, von dem die aktuelle statische Methode tatsächlich aufgerufen wird. Dies scheint kompliziert, teuer und potenziell verwirrend zu sein.
Wir haben über eine einfachere Version diskutiert, die die Einschränkungen des aktuellen Vorschlags wahrt, dass statische virtuelle Member nur auf Typparametern aufgerufen werden können. Da Schnittstellen mit statischen virtuellen Membern häufig einen expliziten Typparameter haben, der einen "Selbsttyp" repräsentiert, wäre das kein großer Verlust: Andere statische virtuelle Member könnten einfach auf diesem Selbsttyp aufgerufen werden. Diese Version ist viel einfacher und scheint ziemlich machbar.
In https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-24.md#default-implementations-of-abstract-statics haben wir beschlossen, Standardimplementierungen statischer Member zu unterstützen, die den in https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/default-interface-methods.md festgelegten Regeln folgen und entsprechend erweitert werden.
Musterabgleich
Für den folgenden Code erwarten Benutzer möglicherweise, dass er „True“ druckt (als ob das konstante Muster inline geschrieben würde):
M(1.0);
static void M<T>(T t) where T : INumberBase<T>
{
Console.WriteLine(t is 1); // Error. Cannot use a numeric constant
Console.WriteLine((t is int i) && (i is 1));
}
Da der Eingabetyp des Musters jedoch nicht double
ist, führt das konstante Muster 1
zuerst eine Typprüfung des eingehenden T
auf int
aus. Da dies nicht intuitiv ist, wird dies blockiert ist, bis eine zukünftige C#-Version ein besseres Handling für den numerischen Abgleich mit Typen bereitstellt, die von INumberBase<T>
abgeleitet werden. Dazu werden wir sagen, dass wir INumberBase<T>
explizit als Typ erkennen, von dem alle "Zahlen" abgeleitet werden, und das Muster blockieren, wenn wir versuchen, ein numerisches Konstantenmuster mit einem Zahlentyp abzugleichen, in dem das Muster nicht dargestellt werden kann (d. h. ein Typparameter, der auf INumberBase<T>
beschränkt ist, oder einen benutzerdefinierten Zahlentyp, der von INumberBase<T>
erbt).
Formal fügen wir eine Ausnahme zur Definition der Musterkompatibilität für konstante Muster hinzu:
Ein Konstantenmuster vergleicht den Wert eines Ausdrucks mit einem konstanten Wert. Die Konstante kann ein beliebiger Konstantenausdruck sein, z. B. ein Literal, der Name einer deklarierten
const
-Variablen oder eine Enumerationskonstante. Wenn der Eingabewert kein offener Typ ist, wird der konstante Ausdruck implizit in den Typ des übereinstimmenden Ausdrucks umgewandelt. Wenn der Typ des Eingabewerts nicht musterkompatibel mit dem Typ des konstanten Ausdrucks ist, endet der Musterabgleich mit einem Fehler. Wenn der abgeglichene konstante Ausdruck ein numerischer Wert ist, der Eingabewert ein Typ ist, der vonSystem.Numerics.INumberBase<T>
erbt, und es keine konstante Umwandlung aus dem konstanten Ausdruck in den Typ des Eingabewerts gibt, endet der Musterabgleich mit einem Fehler.
Außerdem fügen wir eine ähnliche Ausnahme für relationale Muster hinzu:
Wenn es sich bei der Eingabe um einen Typ handelt, für den ein geeigneter eingebauter binärer relationaler Operator definiert ist, der mit der Eingabe als linkem Operand und der angegebenen Konstante als rechtem Operanden anwendbar ist, gilt die Auswertung dieses Operators als Bedeutung des relationalen Musters. Andernfalls wandeln wir mithilfe einer expliziten Nullwert- oder Unboxing-Konvertierung die Eingabe in den Typ des Ausdrucks um. Es handelt sich um einen Kompilierungszeitfehler, wenn keine solche Konvertierung vorhanden ist. Es handelt sich um einen Kompilierzeitfehler, wenn der Eingabetyp ein Typparameter ist, der auf
System.Numerics.INumberBase<T>
beschränkt ist, oder ein Typ, der vonSystem.Numerics.INumberBase<T>
erbt, und wenn der Eingabetyp keinen geeigneten integrierten binären relationalen Operator definiert hat. Das Muster wird als nicht übereinstimmend betrachtet, wenn die Konvertierung fehlschlägt. Wenn die Konvertierung erfolgreich ist, ist das Ergebnis des Musterabgleichsvorgangs das Ergebnis der Auswertung des Ausdrucks e OP v, wobei e die konvertierte Eingabe ist, OP der relationale Operator und v ist der konstante Ausdruck.
Nachteile
- Das Konzept „statisch abstrakt“ ist neu und stellt einen relevanten Beitrag zum Konzept von C# dar.
- Es ist kein einfaches Konzept. Wir sollten sicherstellen, dass es sich lohnt.
Alternativen
Strukturelle Einschränkungen
Ein alternativer Ansatz wäre, "strukturelle Einschränkungen" direkt zu haben und explizit das Vorhandensein bestimmter Operatoren für einen Typparameter zu verlangen. Die Nachteile davon sind: - Dies müsste jedes Mal ausgeschrieben werden. Eine benannte Einschränkung scheint besser zu sein. - Dies ist eine ganz neue Art von Einschränkung, während das vorgeschlagene Feature das vorhandene Konzept von Schnittstelleneinschränkungen verwendet. – Es funktioniert nur für Operatoren, nicht (jedenfalls nicht auf einfache Weise) für andere Arten statischer Member.
Ungelöste Fragen
Statische abstrakte Schnittstellen und statische Klassen
Weitere Informationen finden Sie unter https://github.com/dotnet/csharplang/issues/5783 und https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-16.md#static-abstract-interfaces-and-static-classes.
Designbesprechungen
- https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-02-08.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-05.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-29.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-24.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-16.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-28.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-04-06.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-06-06.md
C# feature specifications