Repository navigation
SupportedFeatures de
🌐 Diese Seite auf: English · Português · Español · Français · Deutsch
Dieses Dokument bietet eine umfassende Liste der C#-Features, .NET-APIs und Muster, die der Compiler von eQuantic.UI unterstützt.
Legende
✅ Volle Unterstützung: Wird zu gleichwertigem JavaScript-Verhalten transpiliert.
⚠️ Teilweise Unterstützung: Funktioniert mit Einschränkungen oder kleinen Unterschieden.❌ Nicht unterstützt: grundsätzlich inkompatible Logik (z. B. blockierende I/O, unsichere Zeiger).
| Feature | Status | Anmerkungen |
|---|---|---|
| Klassen & Structs | ✅ | Werden zu ES6-Klassen transpiliert. Eine einfache Klasse hat ein eigenes Modul, was immer sie deklariert, eine leere über den Standardmitgliedern einer Schnittstelle oder über einer Basis eingeschlossen, und jedes Modul, das sie nennt, importiert sie; eine Klasse, deren Kette von Basen ein Attribut oder einen [ServerOnly]-Typ erreicht, hat keines, drei Ebenen tiefer ebenso wie eine (class Underline : Mark über class Mark : Attribute), eine Basis aus einer referenzierten Bibliothek eingeschlossen. Eine Ausnahmeklasse der Anwendung ist eine Klasse, mit einem eigenen Modul (siehe try / catch / finally). Die Kette wird danach gelesen, wovon jede Basis ableitet, und nie nach ihrem Namen, sodass eine Klasse über einer Schnittstelle, die wie ein Attribut heißt, ihr Modul behält, und ein Name ist überall ein Modul, wo irgendeine Deklaration von ihm eines ist. Eine Klasse, eine statische Klasse, ein Record oder ein Struct, die in einem anderen Typ deklariert sind, sind ebenfalls ein eigenes Modul, benannt nach den Typen, die sie enthalten, und ihrem eigenen Namen, verbunden durch $ (Cart$Item, A$B$C), den kein Typ der obersten Ebene annehmen kann: Jeder Verweis darauf, innerhalb seines Besitzers oder außerhalb, nennt diesen Zwilling, sodass eine verschachtelte Klasse neben einer Klasse der obersten Ebene mit ihrem Namen nie für diese gehalten wird, und eine in einer reinen Serverklasse oder einem Attribut hat keinen, wie ihr Besitzer keinen hat: Client-Code, der sie nennt, wird abgelehnt (EQ2010), und eine darin deklarierte Seite hat keine Route, während eine in einer Ausnahmeklasse so überquert, wie diese Klasse es tut (Seit 0.2.0-preview.61). Ein Record und ein Struct haben einen Zwilling, was immer sie deklarieren, sofern sie nicht mit [ServerOnly] markiert sind, ein Record über einem reinen Server-Record eingeschlossen (Seit 0.2.0-preview.61). |
| Schnittstellen | ✅ | Für die Typprüfung von TypeScript verwendet (zur Laufzeit gelöscht). Ein Standardmitglied (eine Eigenschaft, ein Indexer oder eine Methode mit Rumpf; ein Indexer seit 0.2.0-preview.61) erreicht den Zwilling jeder Klasse, die es übernimmt, ohne es zu deklarieren: aus dem Quelltext der Schnittstelle konvertiert, oder, bei den eigenen Schnittstellen des SDK, die eine Anwendung implementiert (ein Theme, eine Codesprache), an die Kopie der Runtime delegiert. Eine Schnittstelle aus irgendeiner anderen kompilierten Assembly hat keines von beiden, und der Build lehnt die Klasse ab (EQ1008) (Seit 0.2.0-preview.59). |
| Enums | ✅ | Überqueren als ihr camelCase-Name (MainAlign.Start → 'start'), und ein [Flags]-Enum als seine Zahl. .ToString(), Interpolation und Verkettung liefern den C#-Mitgliedsnamen ("Start"), denselben Text, den der Server ausgibt, bei einer Flags-Kombination die gesetzten Flags ("Read, Write"), einen Wert, den kein Mitglied benennt, als seine Ziffern, und nichts für ein nullbares Enum, das null ist. ToString("D"), "X", "F" und "G", das Format einer Interpolation und ihre Ausrichtung schreiben, was .NET schreibt, Enum.Parse, TryParse, GetName, GetNames, GetValues und IsDefined antworten wie die von .NET, und ein Dictionary mit Enum-Schlüssel überquert in beide Richtungen, wobei ein Name, den kein Mitglied hat, abgelehnt wird. Seit 0.2.0-preview.15; die Formate, der Flags-Text, die statischen Mitglieder von Enum und Enum-Schlüssel seit 0.2.0-preview.60. |
| Generics | ✅ | Vollständig unterstützt (zur Laufzeit gelöscht). |
| Erweiterungsmethoden | ✅ | Über das Semantic Model aufgelöst und zu direkten Aufrufen transpiliert. |
| Async / Await | ✅ | Wird auf async / await und Promise abgebildet. |
| Lambda-Ausdrücke | ✅ | Werden auf Pfeilfunktionen () => {} abgebildet. |
| Pattern Matching | ✅ |
is-Muster, Eigenschaftsmuster, rekursive Muster. Ein Typ, an den nichts gebunden wird (o is int or long, int => …, case int:), prüft den Typ, einen long als BigInt, eine Ganzzahl als ganze Zahl und einen char als String aus einer Codeeinheit. Eine Konstante in einem Muster, x is Limits.Max eingeschlossen, wird über ihren Wert geprüft: Ein null passt auf jedes Fehlen, ein decimal und ein NaN vergleichen nach Wert, und eine long-Konstante ist ein BigInt, wie jeder long (Seit 0.2.0-preview.60). Ein Positionsmuster über einem Typ, dessen Deconstruct die Anwendung geschrieben hat, das einer Erweiterung eingeschlossen, ruft es einmal für jeden Wert auf, den ein Test dekonstruiert, auch über die Zweige eines switch und die Alternativen eines or hinweg, wie .NET es tut, und liest die Teile, die es zurückgibt, wie eine Dekonstruktion es tut: Es las die Mitglieder, nach denen die outs benannt sind, was ein Teil, den das Deconstruct berechnet, nicht ist (Seit 0.2.0-preview.61). |
| String-Interpolation | ✅ | Wird auf Template-Literale `${var}` abgebildet. Ein Format mit Text in Anführungszeichen ({date:dd 'de' MMMM}) behält seine Anführungszeichen, und ein interpolierter Raw-String behält seine geschweiften Klammern ($$$"""{{x}}""" ist {{x}}) (Seit 0.2.0-preview.60). Eine Ausrichtung ist die Zahl, die sie ist, eine Konstante über ihren Namen oder ein Ausdruck aus Konstanten eingeschlossen ({name,Widths.Name}, {name,-(Widths.Name + 1)}), auch in einem StringBuilder (Seit 0.2.0-preview.62). |
| String- & char-Literale | ✅ | Ein String oder ein char behält den Wert, den C# liest, wie auch immer er geschrieben ist: ein einzelnes Surrogat ("x\uD83D"), ein Steuerzeichen und die Escapes, die nur C# hat ('\a', '\e', '\x041', '\U00000041'). Ein UTF-8-Literal ("ab"u8) hat keinen Zwilling und lässt den Build scheitern (EQ1004) (Seit 0.2.0-preview.60). |
| Null-Coalescing | ✅ |
?? und ??= werden auf die JS-Entsprechungen abgebildet. |
| Objektinitialisierer | ✅ |
new Obj { Prop = 1 } über einem Record, einem Struct, einer einfachen Klasse, einer Ausnahme oder einem Vokabulartyp, dessen Zwilling die Runtime transpiliert (die Modelle der Tabellenkalkulation und der Formulare, CellRef, FieldError, SheetController…), angewendet, sobald der Konstruktor zurückgekehrt ist, wie C# ihn anwendet (vor 0.2.0-preview.61 nahm eine einfache Klasse ihn als nachgestelltes Konfigurationsobjekt, verwarf eine Ausnahme ihn und übergab ein transpilierter Vokabulartyp ihn an einen Slot, den sein Zwilling nicht hat; eine Komponente nimmt ihn als ihre Props): Jeder Initialisierer des Typs ist gelaufen, der des Mitglieds selbst eingeschlossen, und die Werte werden nach ihnen gelesen. Ein verschachtelter Collection-Initialisierer (Items = { 1, 2 }) fügt der Collection, die das Mitglied hält, etwas hinzu, über das Add, das der gebundene Baum bindet (das einer Erweiterungsmethode über ihr Zuhause, und das einer Liste, einer Menge oder eines ICollection<T>-Mitglieds so, wie ein Aufruf davon es ist); ein verschachtelter Objektinitialisierer (Inner = { N = 3 }) weist in das Objekt zu, das das Mitglied hält; und ein Eintrag ([key] = value) wird über den Indexer des Typs geschrieben. Das Paar eines Dictionarys (Map = { { "a", 1 } }) ist sein Add, das einen schon vorhandenen Schlüssel ablehnt. Jeder Teil wird in der Methode ausgewertet, in der er geschrieben wurde, sodass ein await in einem Element funktioniert, und jedes Element wird angewendet, bevor die Teile des nächsten ausgewertet werden, wobei das Mitglied, dem es hinzufügt oder in das es zuweist, vor seinen eigenen Teilen gelesen wird, wie C# ihn anwendet. Ein Element ohne Absenkung lässt den Build scheitern (EQ1004) (Seit 0.2.0-preview.61). |
field (C# 14) |
✅ | Bewachte Eigenschaften: Der Zwilling erhält einen Slot $name (ein Name, den kein C#-Feld annehmen kann), einen Getter und den eigenen Rumpf des Accessors. Eine statische behält ihren Slot am Typ, die eines Records eingeschlossen, und der Slot beginnt als Initialisierer der Eigenschaft oder als Standardwert ihres Typs (Seit 0.2.0-preview.60). Die Instanzeigenschaft eines Records und eines Structs führt die Rümpfe ihrer Accessoren über dem Slot aus, wie die einer Klasse, und eine, deren Getter einen Rumpf hat, ist Zustand, den ihr Record vergleicht und kopiert; sie wurde vor 0.2.0-preview.61 unter dem Namen der Eigenschaft gehalten, wobei der Rumpf ihres Setters verworfen wurde (Seit 0.2.0-preview.61). |
| Virtuelle und überschriebene Eigenschaften | ✅ | Eine Eigenschaft, die ein abgeleiteter Typ überschreiben kann oder die eine überschreibt (eine als virtual oder override deklarierte Auto-Eigenschaft), hält ihren Wert in einem eigenen Speicher ($name) unter Accessoren auf dem Prototyp des Zwillings, in einer Klasse, einem Record und einem Struct gleichermaßen, sodass jeder Lesezugriff die Überschreibung erreicht und kein Initialisierer den Accessor einer anderen Ebene: Eine Auto-Eigenschaft über einer berechneten, oder eine berechnete über einer Auto-Eigenschaft, warf vor 0.2.0-preview.61 bei new oder antwortete mit dem Wert der Basis. Eine Überschreibung, die nur den Getter einer Eigenschaft deklariert, deren Basis beide hat, behält den Setter der Basis, wie C# ihn erbt. Ein Record vergleicht, hasht und kopiert den Speicher, wie .NET ein Backing Field vergleicht. Ein Zwilling mit Speicher schreibt ihn im JSON unter dem Namen seiner Eigenschaft, sodass eine Server Action die Eigenschaft unter ihrem C#-Namen erhält. Ein Unterschied bleibt: In einem Typ, der eine Auto-Eigenschaft mit einer anderen überschreibt, liest base.Name den Wert der Überschreibung, wo .NET das eigene Feld der Basis liest, da sich beide einen Speicher teilen (Seit 0.2.0-preview.61). |
Verdeckende Mitglieder (new) |
✅ | Eine Methode, die ein geerbtes Mitglied verdeckt, mit new oder mit derselben Signatur und ohne override, wird erreicht, wie C# sie erreicht: Über den Basistyp antwortet die Methode der Basis und über den verdeckenden Typ die verdeckende, bei einem Aufruf, einer Methodengruppe, einem base-Aufruf und ?. gleichermaßen, eine generische Methode über ihrem eigenen Typparameter eingeschlossen, und ebenso ein Mitglied, das die Runtime unter seinem .NET-Namen liest, sodass ((object)c).GetHashCode() das von object ist, über einem public new int GetHashCode(). Ihr Zwilling hält sie unter einem eigenen Namen (name$1), und eine Überschreibung einer new virtual-Methode nimmt den Platz dieser Methode ein. Ebenso eine Methode neben einer expliziten Schnittstellenimplementierung ihres Namens, sodass ein Aufruf über die Klasse sie erreicht und einer über die Schnittstelle die explizite. Eine, die ein Mitglied verdeckt oder neben einer solchen Implementierung steht und einer Schnittstelle antwortet, lässt den Build scheitern (EQ1007), und eine Eigenschaft oder ein Feld, das eines verdeckt, teilt weiterhin seinen Namen mit ihm (#563, seit 0.2.0-preview.61). |
init-Accessoren |
✅ | Als JS-Setter ausgegeben, weil ein init-Rumpf der Ort ist, an dem ein Typ seine Invariante festlegt, und ihn zu verwerfen würde die Prüfung auf dem Client verlieren. |
| Collection-Initialisierer | ✅ |
new List<int> { 1, 2 } wird auf [1, 2] abgebildet. Eine Liste, die mit einem Argument und einem Initialisierer gebaut wird, hält beides, wie C# sie baut: eine Kopie der Quelle, nie die Quelle selbst, oder nichts für eine Kapazität, dann jedes Element der Reihe nach, wobei die Quelle oder die Kapazität zuerst gelesen wird. Eine Kapazität wird trotzdem ausgewertet, und eine negative wird abgelehnt, wie der Konstruktor sie ablehnt. So wird new List<int>(source) { 3 } zu [...source, 3] und new List<int>(10) { 1, 2 } zu [1, 2], ausgeschrieben oder zieltypisiert, und List<int> c = new(source) ist eine Kopie. Eine Kapazität verwarf vor 0.2.0-preview.61 die Elemente, und eine Quelle wurde mit einem Komma mit ihnen verbunden (Seit 0.2.0-preview.61). |
Eigenes IEnumerable<T> |
✅ | Eine Klasse, ein Record oder ein Struct, der IEnumerable<T> oder nur IEnumerable implementiert, wird so aufgezählt, wie sein eigenes GetEnumerator() es sagt, durch foreach, einen Spread, string.Join und jeden LINQ-Operator, jeweils über dasjenige, das C# dafür bindet: Ein foreach über die Klasse durchläuft ihr eigenes öffentliches GetEnumerator() (das eines Struct-Enumerators eingeschlossen), und LINQ, ein Spread, string.Join und ein foreach über die Schnittstelle durchlaufen das von IEnumerable<T>, explizit oder nicht, sodass die beiden verschiedene Sequenzen durchlaufen können, wie in .NET. Es ist die Sequenz, die eine Iteratormethode liefert, oder ein eigener Enumerator, eine Klasse, die ihr eigener Enumerator ist, eingeschlossen, durchlaufen über sein MoveNext und Current und freigegeben, wenn der Durchlauf endet, ein break eingeschlossen. Die Überschreibung einer abgeleiteten Klasse antwortet, das explizite IEnumerable.GetEnumerator() neben dem generischen ist dieselbe Sequenz, und ebenso ein explizites Current neben einem öffentlichen. Eine Klasse mit nur einem öffentlichen GetEnumerator() wird ebenfalls von foreach durchlaufen. Der Zwilling hat eine Iteration und ein Current, sodass ein Typ, der IEnumerable<T> oder IEnumerator<T> über zwei Typen implementiert, den Build scheitern lässt (EQ1007), und ebenso ein explizites IEnumerable.GetEnumerator() oder Current, das anders antwortet als das Mitglied, das der Zwilling behält, während eines, das dessen Antwort zurückgibt (=> GetEnumerator();, => Current;), als eines gehalten wird. Das eigene GetEnumerator() einer .NET-Collection (=> items.GetEnumerator()) hat keine Übersetzung und lässt den Build scheitern (EQ2004): Liefern Sie stattdessen die Elemente per yield (#612, seit 0.2.0-preview.61). |
| Dekonstruktion | ✅ |
var (a, b) = tuple wird auf [a, b] = tuple abgebildet, und ebenso (var c, var d) = … und verschachtelte Bezeichnungen. Ein Record oder ein Struct dekonstruiert sich über das Deconstruct, das er aufruft, in jeder Form: eine Deklaration, eine Zuweisung ((a, b) = point, (var a, b) = point), ein foreach (var (a, b) in points) und eine verschachtelte Dekonstruktion. Das eigene eines Records liest seine Mitglieder, und ein Deconstruct, das die Anwendung geschrieben hat, wird ausgeführt. Die Empfänger und Indizes der Ziele werden vor dem Wert ausgewertet, wie C# sie auswertet, sodass (xs[i++], xs[i++]) = point die Elemente schreibt, die C# schreibt (Seit 0.2.0-preview.60). Jeder Teil erreicht sein Ziel so, wie C# ihn dort ablegt: in den Typ des Ziels konvertiert (ein int-Teil in einen long) und danach geschrieben, was das Ziel ist, der Eintrag eines Dictionarys über seine Klasse, in einer Zuweisung, einer Deklaration und einer Schleife (Seit 0.2.0-preview.60). |
| Ausdrucksvariablen | ✅ | Was ein Muster, ein out var oder eine Dekonstruktion deklariert, wird dort deklariert, wo C# es deklariert, in jeder Art von Mitglied: im umschließenden Block nach einem if oder einer Ausdrucksanweisung, innerhalb einer Schleife oder eines using, und neu bei jeder Schleifeniteration und jedem Aufruf, sodass eine Closure ihren eigenen Wert behält. Ein Name, den JavaScript reserviert (@class), wird einmal umbenannt, an seiner Deklaration und an jedem Verweis (Seit 0.2.0-preview.59). |
| Lokale Funktionen | ✅ | Zu inneren Funktionen transpiliert. Ein out- oder ref-Parameter trägt seinen Wert zum Aufrufer zurück, wie der einer Methode (Seit 0.2.0-preview.60). |
for-Schleifen |
✅ | 1:1. Die Variable einer Schleife gibt es einmal pro Schleife, wie in C#: Eine im Rumpf erzeugte Closure liest ihren aktuellen Wert, nie eine Kopie aus ihrer eigenen Iteration, und ebenso das, was ihr Initialisierer deklariert (out var n, var (i, j) = (0, 3)), ein Kopf aus Ausdrücken eingeschlossen (for (Seed(out var n); …)) (Seit 0.2.0-preview.60). |
| Delegates | ✅ | Werden als Werte aufgerufen, über den Namen, über ein Mitglied oder aus jedem beliebigen Ausdruck (handlers[0](x), Make()(x), seit 0.2.0-preview.60). |
Records (with) |
✅ | Eine Kopie des Records auf seine eigene Klasse, dann die Mitglieder, die der Ausdruck nennt: Wie die Kopie von .NET führt sie keinen Initialisierer und keinen Konstruktor aus. Das with eines Structs, und eines auf den Records der Code-Engine, laufen über die eigene Kopie des Zwillings. Ein Record, dessen Kette einen Kopierkonstruktor deklariert, wird über diesen kopiert, wie das with von C#: Ein deklarierter beginnt seine Mitglieder bei ihrer Null, führt die Kopie seiner Basis mit dem aus, was : base(…) übergibt, und dann seinen Rumpf, und eine synthetisierte Ebene kopiert ihre eigenen Mitglieder (Seit 0.2.0-preview.61). |
| Konstruktoren | ✅ | Der Zwillingskonstruktor eines Records, eines Structs oder einer einfachen Klasse ist der C#-Konstruktor, den der Aufruf bindet, jeder erreicht über die Anzahlen von Argumenten, die er nimmt, seine optionalen Parameter mitgezählt (eine einfache Klasse behielt vor 0.2.0-preview.61 ihren breitesten). Einer, der seine eigene Arbeit tut (der primäre, oder ein expliziter, der nicht verkettet), bindet seine Parameter, führt jeden Initialisierer in Deklarationsreihenfolge aus, den eines Felds und den einer Eigenschaft gleichermaßen, die eines abgeleiteten Typs vor dem Konstruktor seiner Basis, ruft den Konstruktor seiner Basis mit seinen eigenen Argumenten auf und führt seinen Rumpf aus; einer, der mit : this(…) verkettet, führt den aus, an den er verkettet, und dann seinen eigenen Rumpf, selbst wenn der Rumpf jenes Konstruktors früh zurückkehrt. Jedes Argument landet am Platz seines Parameters und wird in der Reihenfolge ausgewertet, in der es geschrieben ist, in new, : this(…), : base(…) und einer Basisklausel gleichermaßen: ein benanntes, ein ausgelassenes optionales und ein params-Array, das als Ganzes übergeben (benannt oder nicht) oder aus seinen Elementen gepackt wird; eine Variable, die ein Argument deklariert (out var n), wird dort deklariert, wo es ausgewertet wird, und der Parameter eines Konstruktors ist eine Variable, der sein Rumpf zuweisen darf. Der Kopierkonstruktor eines Records ist kein Zweig. Zwei Konstruktoren, die dieselbe Anzahl von Argumenten nehmen, lassen den Build scheitern (EQ1009), und ebenso eine Verkettung zu einem Konstruktor, der seinerseits verkettet, und : this() zum impliziten Konstruktor eines Structs neben eigenen Konstruktoren. Die Null eines Structs (default, der Slot eines Arrays, new S() ohne eigenen Konstruktor, die eines generischen Structs, die die Null jedes Typarguments hält, und eine aus einer anderen Assembly eingeschlossen) führt nichts davon aus, seinen statischen Konstruktor eingeschlossen. Der primäre Konstruktor einer einfachen Klasse bindet seine Parameter, wie der eines Records es tut, und ein Parameter, den ein Mitglied liest, wird auf jeder Instanz unter seinem Namen gehalten, sodass einer, der auf dem Namen eines anderen Mitglieds landet, den Build scheitern lässt (EQ1007). Ein Unterschied bleibt: Ein Basiskonstruktor, der ein Mitglied einer abgeleiteten Klasse über ein virtuelles Mitglied liest, liest es ungesetzt, wo .NET es initialisiert liest, da JavaScript nichts die Instanz berühren lässt, bevor super() zurückkehrt (Seit 0.2.0-preview.61). |
| Indexer | ✅ | Ein Instanzindexer ist item(…) und setItem(…, value) seines Zwillings, und jeder Zugriff darauf ruft sie auf: ein Lesen, ein Schreiben, eine Verbundzuweisung, ++ auf einem Wert beliebigen Typs, ??=, ?[…], das Ziel einer Dekonstruktion, ein Schlüssel vom Ende (^1, bei der Anzahl, die sein Typ nennt) und der Eintrag eines Objektinitialisierers, wobei der Empfänger und die Schlüssel jeweils einmal ausgewertet werden, jeder Schlüssel am Platz seines Parameters (ein benannter Schlüssel, ein ausgelassener optionaler Schlüssel, ein params-Schlüssel). Eine Zuweisung antwortet mit dem Wert, den sie zugewiesen hat, was immer der Setter mit seiner Kopie tut, und eine explizite Implementierung des Indexers einer Schnittstelle antwortet auf die Zugriffe der Schnittstelle. Ein zweiter Indexer in einem Typ, oder irgendein Mitglied auf Item oder SetItem neben einem, lässt den Build scheitern (EQ1007) (Seit 0.2.0-preview.61). Ein Zugriff über die Schnittstelle einer Liste (IList<T>, IReadOnlyList<T>, IList) erreicht, was immer die Schnittstelle hält, wenn er läuft, das Element eines Arrays oder einer Liste und item und setItem eines Zwillings gleichermaßen, in jeder dieser Formen, und ihr Count zählt einen eigenen Typ über dessen eigenes Count, nie über ein Length daneben: Die Schnittstelle las vor 0.2.0-preview.61 einen Index und ein length, auf die ein Zwilling nicht antwortet (Seit 0.2.0-preview.61). |
| Bereiche | ✅ |
x[a..b] über einem String, einem Array oder einer Liste ist das slice von JavaScript, und ein Endpunkt, der ^0 sein kann, wird gegen die Länge aufgelöst, wie Index.GetOffset es tut. Über einem eigenen Typ mit einem Length (oder einem Count) und einem Slice(int start, int length) ruft es dieses Slice so auf, wie C# den Bereich absenkt: den Empfänger einmal, dann die Endpunkte in der Reihenfolge, in der sie geschrieben sind, dann die Anzahl, die nur gelesen wird, wo ein Endpunkt vom Ende zählt oder das Ende offen bleibt. So ist strip[1..3] gleich Slice(1, 2), wo es vor 0.2.0-preview.61 das slice(1, 3) des Zwillings aufrief. Ein Bereich, der an einen Indexer übergeben wird, der den Range selbst nimmt, lässt den Build scheitern (EQ2004), wie es ein als Wert gespeicherter Range tut (Seit 0.2.0-preview.61). |
| Statische Initialisierung | ✅ | Die statischen Mitglieder eines Typs beginnen bei ihrer Null, dann laufen die Initialisierer in Deklarationsreihenfolge, dann der statische Konstruktor, einmal, wenn eines von ihnen zum ersten Mal gelesen oder geschrieben wird: static int A = B + 1; static int B = 2; macht A zu 1, wie in .NET, in einem Record, einem Struct, einer Klasse, einer statischen Klasse und einer Komponente. Ein Typ mit einem statischen Konstruktor führt ihn vor seiner ersten Instanz und der ersten Verwendung eines seiner statischen Mitglieder aus, eine Methode, eine berechnete Eigenschaft und ein Operator eingeschlossen. Die Initialisierer laufen in Deklarationsreihenfolge über Felder, Eigenschaften und feldartige Events hinweg, und der Rumpf des statischen Konstruktors nach ihnen, wobei ein return darin nur ihn beendet; einer, der wirft, lässt jede Verwendung des Typs eine TypeInitializationException werfen, wie .NET es tut; ein statisches Mitglied, dessen Initialisierer C# zu einer Konstante faltet, ist deren Wert; und der statische Konstruktor einer Komponente läuft einmal (Seit 0.2.0-preview.61). |
typeof |
✅ | Wird auf ein String-Literal mit dem Typnamen abgebildet. |
| Basistypen | ✅ | Eine Klasse erweitert den Zwilling ihrer Basis, wie auch immer die Basis geschrieben ist: nackt, mit ihrem Namespace, über einen Alias oder mit global:: (Seit 0.2.0-preview.60). |
| Qualifizierte Typnamen | ✅ | Ein Typ, der über seinen Namespace erreicht wird, über einen Teil davon, über das Ganze davon oder über global::, wird so importiert, wie ein über ein using erreichter Typ importiert wird: Innerhalb von App.Chat antwortet Portal.Fold.Text(n), was Fold.Text(n) unter using App.Portal; antwortet (Seit 0.2.0-preview.61). |
base-Aufrufe |
✅ | Wird auf das Schlüsselwort super abgebildet. |
using static |
✅ | Ein Mitglied, das über seinen einfachen Namen erreicht wird, antwortet, was seine qualifizierte Schreibweise antwortet: PI ist Math.PI, Join(…) ist string.Join(…), das High eines Enums ist Level.High, und Now unter using static System.DateTime ist DateTime.Now (Seit 0.2.0-preview.61; bis dahin ließ ein .NET-Mitglied außerhalb von Math, MathF, string, einer Konstante und einem Enum den Build scheitern, selbst wo seine qualifizierte Schreibweise übersetzt wurde). Ein .NET-Mitglied, dessen qualifizierte Schreibweise ebenfalls keine Übersetzung hat, lässt den Build scheitern (EQ2004), statt den Browser zu erreichen (Seit 0.2.0-preview.60). |
Das eigene Count eines Typs |
✅ | Ein Typ, den die Anwendung oder eine Bibliothek, die sie referenziert, deklariert, liest sein eigenes Count: Das Domänenmodell einer Bibliothek las es als length eines Arrays, undefined (Seit 0.2.0-preview.60). |
cast & as |
✅ | Wird auf JS-Durchreichung/Abschneiden abgebildet. |
sizeof |
✅ | Wird auf die Größen der C#-Primitive abgebildet. |
| Anonyme Methoden | ✅ |
delegate(...) { ... } wird auf Pfeilfunktionen abgebildet. |
params |
✅ | Ein REST-Parameter (...xs). Beide Aufrufformen von C# funktionieren: expandierte Argumente und ein als Ganzes übergebenes Array, das gespreizt wird. Seit 0.2.0-preview.15. |
stackalloc |
✅ | Wird auf Typed Arrays abgebildet (z. B. Int32Array). |
yield return |
✅ | Wird auf JS-Generatorfunktionen (function*) abgebildet. |
try / catch / finally |
✅ | Die catch-Klauseln sind EIN JavaScript-catch, das sie in der Reihenfolge von C# probiert: jede nach ihrem Ausnahmetyp, ein davon abgeleiteter Typ eingeschlossen, und nach ihrem when-Filter, der seine eigenen Variablen deklariert (out var n, ein Muster) und false antwortet, wo er wirft, wie der von .NET. Eine Ausnahme, die keine Klausel nimmt, läuft weiter, und throw; wirft die gefangene Ausnahme erneut. Eine Ausnahme ist ein JavaScript-Error, der ihre .NET-Typen trägt, eine eigene Ausnahmeklasse der Anwendung und Exception e = new("…") eingeschlossen, sodass e is ArgumentException, ein switch über Ausnahmen und as ebenfalls den Typ prüfen, und eine Ausnahme, die die Runtime wirft, ist der Typ, den .NET dort wirft (ein Überlauf, ein fehlender Schlüssel, ein ungültiges Format, ein abgelehnter Cast); ein JavaScript-TypeError ist eine NullReferenceException. Ein Unterschied bleibt, der der Plattform: .NET führt einen Filter VOR den finally-Blöcken zwischen dem throw und der Klausel aus, und JavaScript nach ihnen. Seit 0.2.0-preview.60. Die Meldung einer Framework-Ausnahme wird zusammengesetzt, wie .NET sie zusammensetzt: wo keine Meldung oder eine null-Meldung gegeben wurde, der Text, den .NET für diesen Konstruktor schreibt, aus .NET selbst gelesen, dann (Parameter 'x'), und der tatsächliche Wert und der Name eines freigegebenen Objekts auf eigenen Zeilen, die inneren Meldungen einer Aggregatausnahme nach ihrer eigenen und der Satz eines Typinitialisierers, und ParamName, ActualValue, InnerException und TypeName lesen, was der Konstruktor genommen hat, wobei die InnerException einer Aggregatausnahme ihre erste innere ist und auch die eigenen Argumentausnahmen der Runtime ihren Parameter nennen (Seit 0.2.0-preview.61). Ein null-Argument wird abgelehnt, wie .NET es ablehnt, mit einer ArgumentNullException, die den Parameter nennt, von Max, Min und ToDictionary aus LINQ, von einem LINQ-Operator, dem eine Sequenz übergeben wird, die keine Liste ist (second von Concat, first von Zip), von new Guid(text), char.GetUnicodeCategory(text, i), CancellationToken.Register und CreateLinkedTokenSource, und CompareTo(object) antwortet für ein null mit 1, wie das von .NET. Ein Mitglied, das zu einer eigenen Methode von JavaScript abgesenkt wird, LINQ über einer Liste darunter, liest ein null weiterhin durch null, eine NullReferenceException: Die Konformitätssuite ruft jedes Mitglied der übersetzten Oberfläche mit einem null für jeden Referenzparameter auf und listet diese in null-argument-gaps.baseline.txt mit ihren Gründen (Seit 0.2.0-preview.61). Eine Ausnahmeklasse der Anwendung ist eine Klasse über der Ausnahmebasis der Runtime, dem Error, der ihre Typen trägt: Ihre Felder, ihre Eigenschaften, ihre Konstruktoren und ihre Methoden sind ihre eigenen, und ihr Basisaufruf übergibt, was ein new ihrer .NET-Basis übergibt, sodass : base(message, inner) Message und InnerException erreicht, : base("bad", name) über ArgumentException bad (Parameter 'x') und sein ParamName liest, eine Klasse über InvalidOperationException, die keine Meldung angibt, den eigenen Text dieses Typs liest, eine fehlende Meldung über Exception der Standard von .NET ist (Exception of type 'App.Failure' was thrown.), ein eigenes Mitglied unter einem Namen, den die Basis hält (ein Name, eine Überschreibung von Message oder von ParamName), an ihrer Stelle antwortet, und eine generische über ihre Konstruktion gefangen wird (catch (Failed<int>)), ab dem Moment, in dem ihr Konstruktor läuft, ein throw this dort eingeschlossen. Eine fehlende Meldung nennt den Typ, wie .NET ihn zur Laufzeit nennt (App.Failed`1[System.Int32], App.Outer+Inner). Sie wird transpiliert, wie jede Klasse es wird: Eine, die der Browser nie sieht, deren Mitglieder erreichen, was er nicht ausführen kann, wird mit [ServerOnly] markiert, und zwei ihrer Konstruktoren, die gleich viele Argumente nehmen, werden abgelehnt (EQ1009), außer dem, den nur die Serialisierung von .NET aufruft, (SerializationInfo, StreamingContext), den die Vorlage von Visual Studio schreibt und den der Zwilling weglässt, und ein Aufruf dieses Konstruktors, den der Browser machen würde, ein new oder der Basisaufruf eines anderen Konstruktors, lässt den Build scheitern (EQ1009). Eine Ausnahme, die dort gebaut oder geprüft wird, wo ein Typargument, das sie trägt, ein Typparameter ist, lässt den Build ebenfalls scheitern (EQ2014), da JavaScript es löscht: new Failed<T>() in einer generischen Methode, catch (Failed<T>), new Missing() innerhalb der generischen Klasse, die sie deklariert. Eine, die in einem anderen Typ deklariert ist, ist ebenfalls eine Klasse, ihr Zwilling nach ihrem Besitzer benannt (Holder$Nested), und ebenso das, was eine Ausnahmeklasse deklariert, ein Record des Grundes, den sie trägt (Rejected$Reason). In einem Snippet, das für sich allein kompiliert wird, ohne Projekt, aus dem seine Basen gelesen werden könnten, ist eine Ausnahmeklasse der Anwendung weiterhin der Error der Runtime mit ihren Typen und keinem ihrer Mitglieder (#611, seit 0.2.0-preview.61). |
throw-Ausdrücke |
✅ |
x ?? throw …, c ? a : throw … und => throw …, wobei die Ausnahme nur dort ausgewertet wird, wo sie geworfen wird, ein await darin eingeschlossen. Seit 0.2.0-preview.60. |
lock |
Wird allein zum Rumpf transpiliert (JS ist single-threaded). Sein Ausdruck läuft einmal, davor, es sei denn, er liest nur eine Variable, ein Feld oder this (Seit 0.2.0-preview.60; einer, der eine Variable deklariert, seit 0.2.0-preview.59). new object(), das übliche Tor, ist ein frisches Objekt, mit oder ohne leeren Initialisierer. |
Der Compiler enthält spezialisierte Strategien für nahezu alle LINQ-Methoden.
| Logik | Methoden | Status |
|---|---|---|
| Filtern |
Where, OfType
|
✅ |
| Projektion |
Select, SelectMany, Cast
|
✅ |
| Partitionierung |
Skip, Take, SkipWhile, TakeWhile
|
✅ |
| Sortierung |
OrderBy, OrderByDescending, ThenBy, Reverse
|
✅ |
| Aggregation |
Count, Sum, Min, Max, Average, Aggregate
|
✅ |
| Quantoren |
Any, All, Contains
|
✅ |
| Mengen |
Distinct, DistinctBy, Union, Intersect, Except, Concat
|
✅ |
| Elemente |
First, FirstOrDefault, Single, Last, ElementAt
|
✅ |
| Hilfsmittel |
SequenceEqual, DefaultIfEmpty
|
✅ |
| Erzeugung |
Enumerable.Range, Enumerable.Repeat, Enumerable.Empty
|
✅ |
| Konvertierung |
ToList, ToArray, ToDictionary, ToHashSet
|
✅ |
| Gruppierung/Join |
GroupBy, Join, Zip
|
✅ |
Jeder Operator liest seine Quelle als die Sequenz, die .NET aufzählt, wie auch immer der Browser sie hält: ein
Array oder eine Liste so, wie sie sind, ein HashSet, die Paare eines Dictionarys und die sortierte Menge, das sortierte
Dictionary, die sortierte Liste, die Warteschlange, den Stapel und die verkettete Liste der Runtime über ihre eigene Aufzählung (einen Stapel von oben her),
und einen String über seine chars, die UTF-16-Codeeinheiten. ToList und ToArray erzeugen immer ein neues Array, sodass eine
Kopie und ihre Quelle zwei Listen sind. Das foreach und das ToCharArray eines Strings liefern ebenfalls Codeeinheiten, wobei ein
Surrogatpaar zwei chars sind, new string(chars) und new string(chars, start, length) bauen ihren
Text, und eine statische char-Methode, die als Delegate übergeben wird, funktioniert, "a1b2".Count(char.IsDigit) und
Count(IsDigit) unter using static System.Char gleichermaßen (Seit 0.2.0-preview.60).
Enumerable.Range und Repeat werten ihre Argumente einmal aus, dort, wo C# es tut, ein await darunter
eingeschlossen: Range(Start(), 3) ruft Start einmal auf, und Repeat(new List<int>(), 3) wiederholt eine einzige Liste. Eine
negative Anzahl, oder ein Range über int.MaxValue hinaus, wirft die ArgumentOutOfRangeException von .NET
(Seit 0.2.0-preview.60).
Ein Vergleicher, der ToDictionary, ToLookup, GroupBy, Distinct, ToHashSet oder SequenceEqual übergeben wird,
passiert den Zaun, den der Konstruktor einer Collection passiert. Einer, der die Standardgleichheit des Schlüsseltyps verlangt
(EqualityComparer<T>.Default, ein null-Vergleicher oder StringComparer.Ordinal für einen String), antwortet so, wie
der Operator es ohne ihn tut, jeder Selektor in seinem Parameter, ob er benannt ist oder nicht, und jeder andere,
etwa StringComparer.OrdinalIgnoreCase, lässt den Build mit EQ2007 scheitern. Vor 0.2.0-preview.61 lehnten ToDictionary und GroupBy
jeden Vergleicher ab, ToLookup rief einen als Elementselektor auf und Distinct verwarf, was immer
ihm übergeben wurde (Seit 0.2.0-preview.61). Vor 0.2.0-preview.62 antwortete SequenceEqual mit seiner ersten
Sequenz, wenn ihm ein Vergleicher übergeben wurde (Seit 0.2.0-preview.62).
Jede Überladung antwortet wie ihre eigene .NET-Überladung, jedes Argument in dem Parameter, an den es gebunden wird, benannt oder
nicht. FirstOrDefault(defaultValue), LastOrDefault(defaultValue) und SingleOrDefault(defaultValue),
mit Prädikat oder ohne, antworten mit dem Element, wenn es eines gibt, ein null-Element eingeschlossen, und andernfalls mit dem
übergebenen Wert, und SingleOrDefault lehnt ein zweites Element oder einen zweiten Treffer mit der
InvalidOperationException von .NET ab. Zip(second) antwortet mit Paaren und Zip(second, third) mit
Tripeln, gelesen als First, Second und Third oder dekonstruiert, so viele, wie die kürzeste Sequenz enthält.
Average berechnet den Mittelwert im Typ des Elements: ein int oder ein long exakt in einem long summiert, eine
OverflowException über long.MaxValue hinaus, und als double geteilt, ein float-Mittelwert auf einen single gerundet, ein decimal-Mittelwert als decimal,
ein nullbarer nur über seine Werte und null, wenn er keine hat, und eine leere Sequenz mit einer
InvalidOperationException abgelehnt, wo die Antwort nicht nullbar ist. SequenceEqual vergleicht nach der Standardgleichheit
des Elementtyps: ein long und ein decimal nach Wert, ein Record nach seinen Mitgliedern, eine Klasse nach ihrem eigenen
Equals und nach Referenz, wo sie keines hat, ein Array nach Referenz, und NaN gleich sich selbst. Vor 0.2.0-preview.62 wurde ein Standardwert als
Prädikat aufgerufen, die dritte Sequenz eines Zip als Ergebnisselektor, und ein Mittelwert von longs warf einen TypeError
(Seit 0.2.0-preview.62).
Wir bilden gängige .NET-Typen auf ihre JavaScript-Entsprechungen ab.
| .NET-Typ | JavaScript-Entsprechung |
|---|---|
string |
String |
int, double, float
|
Number |
bool |
Boolean |
object |
Object |
dynamic |
any |
- ✅
Join,Format.string.Joinliest jede Sequenz (ein Array, eine Liste, einHashSet, eineLinkedList, die Schlüssel eines Dictionarys, eine Sequenz hinter einer Schnittstelle) und schreibt jeden Wert so, wieToStringvon .NET ihn schreibt: einen bool alsTrue, ein Enum über den Namen seines Mitglieds, einen float über seine eigenen Ziffern, einen double in der Notation von .NET (1E+21), ein null als nichts; einzeln übergebene Werte werden jeweils nach ihrem eigenen Typ geschrieben, und ein null-Trennzeichen schreibt nichts (Seit 0.2.0-preview.61) - ✅
IsNullOrEmpty,IsNullOrWhiteSpace - ✅
Split,Replace,Substring,Trim.Splitantwortet so, wie die Überladung von .NET es tut: Eine Menge von Trennzeichen (ein Array aus chars oder Strings, oder einzeln geschriebene chars) teilt an jedem von ihnen, wobei an einer Stelle das erste in der Reihenfolge der Menge gewinnt; ein null- oder leeres einzelnes Trennzeichen teilt nichts, und eine null- oder leere Menge teilt am Leerraum von .NET; die Anzahl begrenzt die Einträge, wobei der letzte den Rest nimmt;TrimEntriesundRemoveEmptyEntriesgelten, ausgeschrieben oder in einer Variable gehalten; und eine negative Anzahl oder eine undefinierte Option wirft die Ausnahme von .NET (Seit 0.2.0-preview.62) - ✅
ToLower,ToUpperund ihr invariantes Paar ändern die Groß-/Kleinschreibung wie .NET 10: Jede Codeeinheit, oder jedes Surrogatpaar, wird zu genau einer anderen (ßundfibleiben, wie sie sind,ᾳwird in Großschreibung zuᾼ, und ein großes Sigma wird in Kleinschreibung zuσ, wo immer es steht), und die beiden i sind die der Kultur: tr und az schreibenigroß alsİundIklein alsı, jede andere Kultur schreibtıgroß alsIundİklein alsi, und die invariante Kultur lässtİundı, wie sie sind. Ein Buchstabe, dessen Großbuchstabe mit Unicode 16 kam, etwaƛ, behält seine Schreibung, wie .NET 10 sie auf einem Linux- oder Windows-Server behält, dessen ICU älter ist; .NET auf macOS 26 ändert sie, nach Apples ICU. Ein Kulturargument überquert alsCultureInfo.CurrentCultureoder ein null, die die Kultur der Seite sind, und alsCultureInfo.InvariantCulture; jedes andere lässt den Build scheitern (EQ2108) (Seit 0.2.0-preview.62, wo der BrowserßzuSSverlängerte, ein finales Sigma in seinem Kontext las, keine Kultur kannte und die verwarf, die der Aufruf nannte) - ✅
StartsWith,EndsWith - ✅
Equals,StartsWith,EndsWith,Contains,IndexOf,LastIndexOfundReplacemit einemStringComparison, nach diesem Vergleich, undCompareTonach der aktuellen Kultur; eine Suche nach einem Kulturvergleich lässt den Build scheitern (EQ1004) (Seit 0.2.0-preview.60)
Die Zeittypen stützen sich auf tickgenaue Kompatibilitätstypen (100-ns-Ticks, proleptischer gregorianischer
Kalender, nicht die verlustbehaftete Abbildung auf new Date() / numerische Millisekunden). Konstruktoren, Komponenten, Add*,
Arithmetik (-→TimeSpan), Vergleiche und .ToString() stimmen alle mit .NET überein, wobei der Text eines Datums
über einen einzigen Formatierer in der geltenden Kultur geschrieben wird (Seit 0.2.0-preview.61). Ein TimeSpan schreibt das
Format, das ihm gegeben wird, über ToString, ein Interpolationsloch und einen Platzhalter gleichermaßen: c, t und T seine konstante
Form, g und G seine allgemeinen mit dem Dezimaltrennzeichen der Kultur, ein benutzerdefiniertes Format wie
hh\:mm Token für Token, und ein Format, das .NET ablehnt, seine FormatException in den Worten von .NET
(Seit 0.2.0-preview.62, wo jedes Format die konstante Form schrieb); Werte überqueren die SSR-Leitung als
ISO-8601- / "c"-Strings und werden zurück in den Kompatibilitätstyp hydriert. TryParse(text, out var value) jedes
der fünf antwortet mit true und dem Wert, den sein Parse liest, oder mit false und dem Standardwert des Typs in
value für einen Text, den Parse ablehnt, und für einen null-Text, wie das von .NET (Seit 0.2.0-preview.62, wo
vier von ihnen die Seite scheitern ließen und das von DateOnly mit null antwortete). Parse und TryParse lesen in der
Kultur, die der Aufruf nennt, wie .NET es tut: ohne Provider, mit CultureInfo.CurrentCulture oder mit einem null-Provider die
Kultur der Seite, und mit CultureInfo.InvariantCulture die invariante. Sie lesen die numerischen
Formen, die die Kultur schreibt: ein Datum aus drei Zahlen in der Reihenfolge ihres kurzen Datumsmusters (01/02/2024
ist der 1. Februar in pt-BR und der 2. Januar in en-US), ihre Trennzeichen neben /, - und
., eine Uhrzeit mit ihrem eigenen Trennzeichen (21.30 von fi-FI, wo drei kurze, durch Punkte verbundene Zahlen
eine Uhrzeit sind und 10.11.12 10:11:12 ist) und ihre eigenen AM- und PM-Bezeichner neben AM und PM, vor oder
nach der Uhrzeit, die Uhrzeit vor oder nach dem Datum, einen Bruchteil nach den Sekunden mit einem Punkt oder einem
Komma, eine Zone und die ISO-Form (2024-01-15T09:30:00Z).
Ein TimeSpan wird gelesen, wie .NET einen ohne Format liest: danach, wie viele Zahlen er hat und was
zwischen ihnen steht, gegen das invariante Muster d.hh:mm:ss.fffffff und das eigene der Kultur, d:hh:mm:ss
und ihr Dezimaltrennzeichen, sodass 10 zehn Tage ist, 1:2 eine Stunde und zwei Minuten, 1:02:03:04 ein Tag und
zwei Stunden, und 24:00:00, das keine Uhr liest, vierundzwanzig Tage; ein Teil über seiner Grenze oder eine Zeitspanne
über dem Bereich des Typs wirft die OverflowException von .NET in dessen Worten, und ein Bruchteil über sieben Stellen hinaus
wird gerundet, wie .NET ihn rundet (Seit 0.2.0-preview.62, wo nur [-][d.]hh:mm:ss[.f] gelesen und
kein Teil geprüft wurde, sodass 00:60:00 eine Stunde war). So wird ein mit ToString() geschriebener Wert zurückgelesen, und was abgelehnt wird, wird in den Worten von .NET abgelehnt. Jede andere Kultur lässt
den Build scheitern (EQ2108), und ebenso andere Stile als Leerraum und ein Span (EQ1004); ParseExact,
TryParseExact und GetDateTimeFormats werden nicht übersetzt (EQ2004). Convert.ToDateTime liest einen Text
wie DateTime.Parse, einen null-Text als MinValue, ein Objekt nach dem, was es hält, und ein Datum als es selbst,
und ein Wert eines anderen Typs wirft die InvalidCastException von .NET; ein in einem Objekt gehaltener char wird als
Text aus einem Zeichen gelesen, da ein geboxter Wert seinen Typ nicht trägt
(#552) (Seit 0.2.0-preview.62, wo jeder
Leser die invarianten Formen las, ein Provider die Seite scheitern ließ und Convert.ToDateTime mit dem
Text antwortete). Der Name eines Monats, ein Datum aus zwei Zahlen, eine Uhrzeit ohne Datum und andere Kalender als der
gregorianische werden noch nicht gelesen (#807). Ein
Konstruktor, der einen Calendar nimmt, lässt den Build scheitern (EQ1004), da der Browser keinen der Kalender von .NET hat,
den von DateOnly eingeschlossen (Seit 0.2.0-preview.62, wo er den Kalender verwarf).
Die Details je Typ stehen in der Tabelle .NET-BCL-Abdeckung & Konformität unten.
- ✅
List<T>→ JavaScript-Array[];Removeantwortet mit dem bool, mit dem .NET antwortet, und vergleicht, wieEqualityComparer<T>.Defaultvergleicht, einen Record nach Wert, ein Tupel Element für Element und NaN mit NaN (Seit 0.2.0-preview.59).IndexOf,LastIndexOf,ContainsundRemove,Array.IndexOfundArray.LastIndexOfsowieContainsaus LINQ vergleichen nach diesem einen Vergleich, der nach dem Elementtyp gewählt wird: ein NaN, ein Record, ein Tupel und ein decimal nach Wert gefunden, eine Klasse nach ihrem eigenenEquals, das Array-Mitglied eines Tupels nach Referenz, wieValueTuple.Equalses vergleicht.SortundArray.Sortsind die introspektive Sortierung von .NET, instabil wie die von .NET, sodass gleiche Elemente dort landen, wo die von .NET landen, nach dem Standardvergleicher des Typs (die NaNs eines double zuerst), einemComparison<T>, den sechs einesStringComparer,Comparer<T>.Createoder einem eigenen Vergleicher, mit den Ausnahmen von .NET; jeder andere Vergleicher lässt den Build scheitern (EQ2007).BinarySearchantwortet bei einem Fehlschlag mit dem Komplement der Einfügeposition,FindundFindLastmit dem Standardwert des Elementtyps,RemoveAlldamit, wie viele es entfernt hat,CopyToschreibt in das Array, das ihm übergeben wird, und die Bereiche vonFindIndex,FindLastIndex,IndexOfundLastIndexOfwerden in den Worten von .NET geprüft (Seit 0.2.0-preview.61) - ✅
AddundClearvonICollection<T>fügen hinzu und leeren, wie es die Collection hinter der Schnittstelle tut, wenn der Aufruf läuft: Eine Liste hängt an, eine Menge fügt einen Wert hinzu, den sie nicht hält, eine verkettete Liste fügt am Ende hinzu, ein Dictionary fügt das Paar hinzu und lehnt einen schon vorhandenen Schlüssel in den Worten von .NET ab, und ein eigener Typ führt sein eigenesAddundClearaus, den Collection-Initialisierer eines Mitglieds (Items = { 1, 2 }) eingeschlossen. Vor 0.2.0-preview.61 waren siepushundspliceeines Arrays, die einer Menge, einer verketteten Liste und einem Dictionary fehlen (Seit 0.2.0-preview.61) - ✅
Dictionary<TKey, TValue>,IDictionaryundIReadOnlyDictionary→ die Dictionary-Klasse der Runtime ($eq.collections.dictionary), die ihre Einträge nach Slot hält, wie die von .NET: Sie zählt in Einfügereihenfolge auf, und ein Eintrag, der nach einer Entfernung hinzugefügt wird, nimmt den Platz des entfernten ein, der zuletzt freigegebene zuerst, sodassd[3] = 30; d[1] = 10;3,1aufzählt. Jeder Schlüssel behält seinen Typ (einint-Schlüssel kommt als Zahl zurück, einlongals BigInt, eincharals char) und wird gefunden, wie der Standardvergleicher des Schlüsseltyps ihn findet: nach Wert bei einem Record, einem Struct, einem Tupel, einem decimal oder einem Datum, nach Identität bei einer Zahl, einem String, einem char, einem bool, einem Enum oder einerGuid, und nach der eigenen Gleichheit des Werts, wo der Typ nicht entscheidet (object, eine Schnittstelle, ein Typparameter, eine Klasse), sodass zwei gleiche Records unterobjectein Schlüssel sind; das Array-Element eines Tupelschlüssels nach Referenz (Seit 0.2.0-preview.61).new Dictionary<K, V>(other)kopiert, ein dekonstruierendesforeach (var (k, v) in d)funktioniert auf jedem Dictionary, die sortierten eingeschlossen,TryAdd,ContainsValue,Remove(key, out value),Keys.ContainsundKeys.Countantworten wie die von .NET, undToDictionarybaut dieselbe Klasse.Add, ein Collection-Initialisierer und der Konstruktor, der Paare kopiert, lehnen einen schon vorhandenen Schlüssel mit derArgumentExceptionvon .NET ab, in den Worten der jeweiligen Collection, während der Indexer, das[key] = valueeines Objektinitialisierers undTryAddersetzen oder ablehnen, undnew KeyValuePair<K, V>(key, value)undKeyValuePair.Createdas Paar bauen, das ein Dictionary liefert (Seit 0.2.0-preview.60). Ein Schlüssel, der hinzugefügt wird, während einforeachdas Dictionary durchläuft, beendet den Durchlauf mit einerInvalidOperationException, wie es der Enumerator von .NET tut, während ein Überschreiben, ein Entfernen undClearweiterlaufen, und ein sortiertes Dictionary und eine sortierte Liste lehnen jede Änderung ab, auch beim Durchlaufen ihrer Schlüssel und Werte, in den Worten von System.Collections, wo beide leben, wobei dasRemoveeines Schlüssels, den ein sortiertes Dictionary nicht hält, und seinAddeines, den es hält, so gezählt werden, wie sein Baum sie zählt, und seinRemove(key, out value)eines Schlüssels, den es nicht hält, als nichts, daCollectionExtensionszuerst fragt (Seit 0.2.0-preview.64, #864). Ein Dictionary im serverseitig gerenderten Zustand, im Ergebnis oder in den Argumenten einer Server Action oder in der Payload eines Topics überquert als seine[key, value]-Paare und kommt als die Klasse an, seine Schlüssel in ihrem Typ, in der Reihenfolge, in der es aufzählte, Ganzzahlschlüssel eingeschlossen: Ein JSON-Objekt hätte diese zuerst und aufsteigend aufgelistet (Seit 0.2.0-preview.61, #437). Ein Vergleicher, der verlangt, was der Browser schon tut (null,EqualityComparer<T>.Default,StringComparer.Ordinal), baut das Dictionary, und ein sortiertes behält die Reihenfolge, die er verlangt (Seit 0.2.0-preview.61), während einer, der die Gleichheit ändert, etwaStringComparer.OrdinalIgnoreCase, den Build mitEQ2007scheitern lässt. Seit 0.2.0-preview.60. SeineKeysundValuessind Live-Ansichten, wie die von .NET: Eine, die gelesen wird, nachdem sich das Dictionary geändert hat, zeigt die Änderung, ein Schlüssel, der hinzugefügt wird, während eine durchlaufen wird, beendet den Durchlauf, das eigeneContainsder Schlüssel findet einen Schlüssel, wie das Dictionary es tut, und jedes Element kommt zurück, wie es ist, ein Delegate eingeschlossen. Die eines sortierten Dictionarys und einer sortierten Liste sind es auch, wo sie Kopien waren, und eine Änderung, die von einer Ansicht verlangt wird, einAddoder einRemoveüberICollection<T>, ob die Ansicht den Wert hält oder nicht, oder ein Schlüssel, der überKeys[i]einer sortierten Liste geschrieben wird, wirft dieNotSupportedException, die .NET wirft, in den Worten der jeweiligen Collection;TryAddeines sortierten Dictionarys, das C# inCollectionExtensionsfindet, war einReferenceError, und eine eigene Erweiterungsmethode namensTryAddoderGetValueOrDefaultwird selbst aufgerufen, wo sie für die des Dictionarys gehalten wurde (Seit 0.2.0-preview.64, #864). Eine LINQ-Abfrage über einer Ansicht liest sie, wie über jeder Quelle, vollständig vor ihrem ersten Callback (#685).EnsureCapacityantwortet mit der Primzahl, auf die sich .NET festlegt, ein Kapazitätskonstruktor und eine Kopie dimensionieren das Dictionary, wie die von .NET es tun, undTrimExcesspackt die Einträge, sodass der nächste Schlüssel nach den anderen kommt (Seit 0.2.0-preview.61). - ✅
HashSet<T>,ISet<T>undIReadOnlySet<T>→ die Menge der Runtime ($eq.collections.hashSet), gebaut, wo immer eine erzeugt wird (ein Konstruktor, ein Initialisierer, ein Collection-Ausdruck,ToHashSet, ein Wert, der vom Server herüberkam). Sie hält ihre Elemente nach Slot in der Tabelle, in der das Dictionary der Runtime seine Schlüssel hält: Ein Element, das nach einer Entfernung hinzugefügt wird, nimmt den Slot des entfernten ein, der zuletzt freigegebene zuerst, sodass{ 3, 1 }minus 3, plus 5,5,1aufzählt, und jede Mengenoperation (UnionWith,IntersectWith,ExceptWith,SymmetricExceptWith,RemoveWhere), eine Kopie undTrimExcesslassen die Slots dort, wo die von .NET sie lassen. Ein Element wird gefunden, wie der Standardvergleicher seines Typs es findet, nach der Regel, der die Schlüssel eines Dictionarys folgen: ein Datum, ein decimal, ein Tupel und ein Record nach Wert, NaN gleich NaN.Addantwortet, ob das Element neu war, die Mengenprädikate,CopyToundEnsureCapacityantworten wie die von .NET, und ein Element, das während einesforeachhinzugefügt wird, beendet den Durchlauf mit einerInvalidOperationException.TryGetValuewird noch nicht behandelt und lässt den Build scheitern (EQ1004); ein Vergleicher, der die Gleichheit ändert, lässt ihn ebenfalls scheitern (EQ2007) (Seit 0.2.0-preview.61). - ✅
ContainsKey/TryGetValueantworten nie für"constructor","toString"oder irgendetwas anderes aufObject.prototype. Seit 0.2.0-preview.15;GetValueOrDefaultebenfalls, seit 0.2.0-preview.58. - ✅
TryGetValueundGetValueOrDefaultantworten auf einen Fehlschlag mitdefault(TValue)auf jedem Dictionary, die sortierten eingeschlossen:0für einenint,falsefür einenbool,nullfür eine Referenz, sodassd.TryGetValue(k, out var n); d[k] = n + 1;zählt. Der Empfänger, der Schlüssel und ein expliziter Standardwert werden jeweils einmal ausgewertet, in der geschriebenen Reihenfolge; einout _empfängt nichts, und einout-Feld oder -Element wird dort geschrieben, wo C# es schreibt. Die statische Form,CollectionExtensions.GetValueOrDefault(d, key), liest das Dictionary, das ihr erstes Argument übergibt. Seit 0.2.0-preview.58. - ✅
Queue<T>,Stack<T>,LinkedList<T>,SortedSet<T>,SortedDictionary<K,V>,SortedList<K,V>→ Kompatibilitäts-Collections der Runtime unter$eq.collections.*. Eine sortierte ordnet, wieComparer<T>.Defaultihren Elementtyp ordnet: einen String in der aktuellen Kultur, einen double mit seinem NaN zuerst, ein Enum nach seinem Wert, einen decimal, ein Datum und einen eigenen Typ, derIComparableimplementiert, nach seinemCompareTo, undStringComparer.Ordinalnach Codeeinheit.Containseiner Warteschlange und eines Stapels findet einen decimal, ein Datum oder einen Record nach seinem Wert (Seit 0.2.0-preview.60). Jede schreibt das JSON, das .NET schreibt, ein Array in der Reihenfolge, in der sie aufzählt, einen Stapel von oben her, sodass ein Argument einer Server Action den Server als die Collection erreicht (Seit 0.2.0-preview.61).CopyToeiner Warteschlange und eines Stapels sowieCopyTo,UnionWith,IntersectWith,ExceptWith,SymmetricExceptWith,IsSubsetOf,IsProperSubsetOf,IsSupersetOf,IsProperSupersetOf,Overlaps,SetEqualsundRemoveWhereeiner sortierten Menge antworten wie die von .NET, ihre Ablehnungen in den Worten von .NET, und von zwei Elementen, die eine sortierte Menge für gleich hält (1.0mund1.00m), behalten eine Vereinigung und eine Schnittmenge dasjenige, das .NET behält. Eine null-Quelle, die dem Konstruktor einer Liste, einer Warteschlange, eines Stapels, einer verketteten Liste, einer sortierten Menge oder irgendeines Dictionarys übergeben wird, wirft dieArgumentNullException, die .NET wirft, und nenntcollectionoderdictionary, wo der Browser eine leere baute (eine Liste warf einenTypeError), eine negative Kapazität einer Warteschlange oder eines Stapels wird abgelehnt, und ein in eine davon kopierter String wird über seine UTF-16-chars gelesen (Seit 0.2.0-preview.62, #723, #726). Einforeachüber einer Warteschlange, einem Stapel, einer verketteten Liste oder einer sortierten Menge endet beim Schritt nach der Änderung der Collection, den nach dem letzten Element eingeschlossen, mit derInvalidOperationException, die .NET wirft, in den Worten von .NET: Eine Änderung ist, was .NET als eine zählt, dasAddeines Elements, das eine sortierte Menge schon hält, und ihrRemoveeines Elements, das sie nicht hält, eingeschlossen, und ein Wert, der in den Knoten einer verketteten Liste geschrieben wird, ist keine.Reverse()einer sortierten Menge durchläuft sie von ihrem größten Element aus und wird ebenso abgelehnt (Seit 0.2.0-preview.62, #765).TryDequeueundTryPeekeiner Warteschlange,TryPopundTryPeekeines Stapels undTryGetValueeiner sortierten Menge antworten über ihroutwie die von .NET, mit dem Standardwert des Typs, wo nichts da ist;MinundMaxeiner leeren sortierten Menge sind der Standardwert ihres Elementtyps, wie die von .NET;Capacity,EnsureCapacityundTrimExcesseiner Warteschlange und eines Stapels halten die Kapazität von .NET, gewachsen nach der eigenen Regel der jeweiligen und aus einer Sequenz dimensioniert, wie .NET sie dimensioniert, auf die Anzahl einerICollection<T>und durch Verdoppeln ab vier bei allem anderen;Find,FindLast,AddAfter,AddBeforeundCopyToeiner verketteten Liste, und die Überladungen, die einen Knoten nehmen, verknüpfen wie die von .NET, wobei ein entfernter oder geleerter Knoten zu keiner Liste gehört, mit den Ablehnungen von .NET in dessen Worten; undnew LinkedListNode<T>(value)baut einen Knoten. Jedes andere Mitglied der fünf, darunterComparereiner sortierten Menge und ein explizitesGetEnumerator, lässt den Build scheitern (EQ2004), wo jedes Mitglied kompilierte und die, die der Runtime fehlten, im Browser ein TypeError waren. Eine Warteschlange oder ein Stapel, aus einer LINQ-Abfrage gebaut, nimmt die Anzahl der Abfrage als Kapazität, da die Abfrage vollständig gelesen wurde (#685) (Seit 0.2.0-preview.62, #866).GetViewBetweeneiner sortierten Menge antwortet mit einer Ansicht dieses Bereichs der Menge, die die Elemente der Menge teilt, sodass jede sieht, was die andere ändert; sie lehnt ein Element außerhalb ihres Bereichs und Grenzen in falscher Reihenfolge mit den Ausnahmen von .NET ab, in dessen Worten, und nimmt die Wege von .NET durch die Mengenoperationen, wobei ihrExceptWithdie Anzahl fragt, die sie zuletzt gezählt hat, sodass sie nichts entfernt, bis ihre Anzahl gelesen wurde, überCount, LINQ, eine Kopie in eine Liste oder eine Menge oder eine Mengenoperation, die sie nach ihrer Anzahl fragt, wie die von .NET (Seit 0.2.0-preview.64, #865).RemoveWhereeiner sortierten Menge fragt sein Prädikat in der Reihenfolge der Menge ab, wo .NET seinen Baum in Breitensuche durchläuft (#766). Eine LINQ-Abfrage über einer Ansicht zählt sie erneut, woFirstoderWherevon .NET sie nur durchlaufen, sodass ihrExceptWithentfernen kann, was das von .NET, mit einer nie gelesenen Anzahl, stehen lässt, undAddRangeeiner Liste zählt sie nicht, wo das von .NET es tut (#865).
- ✅
Task,Task<T>→Promise. - ✅
Task.Delay→ Wrapper umsetTimeout. - ✅
Task.WhenAll,Task.WhenAny. ⚠️ Task.Runläuft auf dem Haupt-Thread (Microtask), NICHT auf einem Hintergrund-Thread.
- ✅
Console.WriteLine→console.log. - ✅
Math.*(Min, Max, Abs, Round usw.) →Math.*. - ✅
Guid(NewGuid,Empty,Parse,TryParse,new Guid(text)) →crypto.randomUUID()für eine neue, und ihr kanonischer Text, das kleingeschriebeneD-Format, für eine, die aus Text in irgendeinem Format gelesen wird, das .NET liest, sodass zwei Schreibweisen einer Guid für==, ein Dictionary und eine Menge ein einziger Wert sind (Seit 0.2.0-preview.60). - ✅
Regex→ JavaScript-RegExp.
eQuantic.UI.Lucide / Heroicons / RadixIcons / TablerIcons / Phosphor / SimpleIcons / BootstrapIcons / Iconoir / ...
Zweck: Umfassende Icon-Sets. Enthält:
- Logik zur SVG-Auflösung
- Spezialisierte Icon-Komponenten
- Ein generiertes
IconGlyphpro Icon, als Eigenschaft, damit der Trimmer die entfernen kann, die niemand nennt
| Paket | Zweck | Status |
|---|---|---|
| eQuantic.UI.Charts | Einmal geschriebene Diagramme, von jedem Realisierer gezeichnet. | ✅ |
| eQuantic.UI.Lottie | Hochperformante Animationen. | ✅ |
| eQuantic.UI.Image | Optimiert (Lazy/Blur/Priority). | ✅ |
| Muster | Schnittstelle | Beschreibung | Status |
|---|---|---|---|
| Metadatenverwaltung | IHandleMetadata |
SEO-Tags und Head-Elemente. | ✅ |
| Asset-Verwaltung | IRequireAssets |
Dynamische Script-/Style-Injektion. | ✅ |
| Server Actions | [ServerAction] |
Sicheres RPC vom Client zum Server. | ✅ |
| Zusammengesetzte Komponenten | entfällt | Semantische Muster für Unterkomponenten. | ✅ |
| Gemeinsame Klassenbibliothek | ProjectReference |
Ein Typ eines referenzierten Projekts, den eine Seite verwendet, ist ein Modul des Bundles: eine statische Klasse, ein Record, eine Komponente und ihre Strings. Siehe Build-Ablauf (Seit 0.2.0-preview.62). | ✅ |
-
Blockierender Code:
- ❌
.Wait(),.Resultauf Tasks werden NICHT unterstützt. Sie müssenawaitverwenden. Das Blockieren des Haupt-Threads friert die Browser-Oberfläche ein.
- ❌
-
Reflection:
- ❌
System.Reflectionwird weitgehend nicht unterstützt. - ✅
typeof(T).Nameundnameof(...)sind unterstützte Konstanten.
- ❌
-
Dateisystem:
- ❌
System.IO(File,Directory) in einer Client-Komponente ist ein Build-Fehler (EQ2112): Der Browser hat kein Dateisystem, das er ihm geben könnte. - Verwenden Sie Server Actions, um Dateioperationen abzuwickeln.
- ❌
-
Zahlen:
- ✅
int/double/floatwerden auf JS-Zahlen abgebildet. Ganzzahldivision schneidet ab (Math.trunc), und eine Division oder ein Rest durch null, oderMinValuedurch -1 beiintundlong, wirft die Ausnahme, die .NET wirft (Seit 0.2.0-preview.58); einfloatwird überall, wo er erzeugt wird, auf einfache Genauigkeit gerundet, genau so, wie RyuJIT ihn rundet, die Verbundzuweisung und das Inkrement einesfloat?-Ziels eingeschlossen (Seit 0.2.0-preview.58), undMath.Roundist das von .NET: ein exakter Mittelpunkt, jederMidpointRounding-Modus. - ✅
decimalist durchgehend exakt: kompiliert zum KompatibilitätstypDecimalder Runtime (BigInt-Mantisse + Skala), sodass0.1m + 0.2m == 0.3mtrueist. Decimals überqueren die Leitung außerdem als JSON-Strings (EqJson) und werden auf dem Client zurück inDecimalhydriert (derDecimal-Standardwert des Felds steuert eine typerhaltende Umwandlung, siehehydrate-value.ts), sodass vom Server gelieferte Decimals alle 28 Stellen behalten, statt über einen double gerundet zu werden. Ein aus Text gelesener decimal (decimal.Parse/TryParse,Convert.ToDecimal("…")) folgt der Grammatik von .NET unter denNumberStyles, die der Aufruf nennt, und rundet, wie der Parser von .NET rundet. Er wird in der invarianten Kultur gelesen, und der Aufruf sagt das mitCultureInfo.InvariantCulture: Ohne Kultur warnt der Build (EQ2110), und mit einer anderen scheitert er (EQ2108), da der Browser den Text keiner anderen Kultur liest. Ein in einen decimal konvertierterdoubleoderfloatbehält die 15 bzw. 7 signifikanten Stellen von .NET:(decimal)(0.1 + 0.2)ist0.3. Seit 0.2.0-preview.58. - ✅ Eine Konstante ist ihr Wert in ihrem C#-Typ:
decimal.MaxValue,MinValue,Zero,OneundMinusOnesind exakte Decimals, geschrieben alsdecimaloderDecimal, und einelong-Konstante wieTimeSpan.TicksPerSecondist ein BigInt, sodass sie auf jeden anderen long trifft. Ein decimal-Literal ist sein Wert, sodass1_000.5mfunktioniert, und der Standardwert eines Parameters, eingesetzt für ein Argument, das ein benanntes überspringt, behält seinen Typ: ein decimal, ein long, ein char, ein String mit einem Anführungszeichen darin. Eine Konstante, die über ihren bloßen Namen unter einemusing staticerreicht wird, wird genauso geschrieben. Eine Konstante eines Enum-Typs ist ein Wert dieses Enums, nicht seine Zahl, und eine decimal-Konstante passt nach Wert in einem Muster, einem switch-Zweig oder einemcase(d is 1m,case decimal.One:). Seit 0.2.0-preview.60. - ✅ Auch jeder andere numerische Typ liest Text wie .NET:
Parse/TryParsevonint,uint,long,ulong,short,ushort,byte,sbyte,doubleundfloatsowieConvert.ToInt32("…")und seine Geschwister lesen den ganzen Text unter den eigenenNumberStylesdes Typs (Integer, oderFloat | AllowThousandsfür eine reelle Zahl) oder denen, die der Aufruf nennt. Text, der keine Zahl ist, wirft eineFormatException("12abc","1e3"als int,"0x1F"ohneHexNumber), ein Wert, den der Typ nicht halten kann, eineOverflowException, ein gescheitertesTryParsehinterlässt0, ein Hex- oder Binärstil liest die Bits des Typs ("FFFFFFFF"ist-1für einen int), der Text eines long ist ein BigInt, und ein float wird einmal gerundet, aus den Ziffern. Die Kultur ist die von decimal: EQ2110 ohne, EQ2108 mit jeder außerCultureInfo.InvariantCulture. Seit 0.2.0-preview.58. - ✅ Der Text eines
doubleund einesfloatist der von .NET: die kürzesten Ziffern, die als der Wert zurückgelesen werden, Festkommanotation von 1e-4 bis 1e16 für einen double und 1e8 für einen float, wissenschaftliche außerhalb (1E+17,1E-05),-0für eine negative Null undInfinityfür ein Unendlich, dasselbe überToString(),ToString(CultureInfo.InvariantCulture), eine Verkettung und eine Interpolation; einfloat?oderdouble?, der null ist, schreibt nichts. Seit 0.2.0-preview.58. - ✅ Der Text einer Zahl ohne Spezifizierer ist der der Kultur, wo immer C# ihn schreibt: eine
Verkettung, ein einfaches oder ausgerichtetes Interpolationsloch,
ToString(),Convert.ToString,string.Format("{0}"),string.Concat,string.Join,AppendundInserteinesStringBuilderund der Text eines Records, wie .NET sie in der geltenden Kultur schreibt:-1,5in pt-BR, ein Minuszeichen, wie die Kultur es schreibt (−5von sv-SE), ein float in seinen eigenen Ziffern, ein decimal mit seiner Skala, und nie-0für eine Ganzzahl, die JavaScript als negative Null hält. Ein für eine Maschine geschriebener Wert sagt das mitToString(CultureInfo.InvariantCulture), das seine Form in jeder Kultur behält. Seit 0.2.0-preview.61. - ✅ Auch über den Formatierer: ein float, der mit einer Ausrichtung oder einem Spezifizierer formatiert wird
(
$"{f,8}",$"{f:G}"),ToString("G"),ToString("R")und ein float, der anstring.Formatübergeben wird, schreiben seine eigenen Ziffern,GundRin der Notation von .NET (1E+21). Einstring.Format-Platzhalter richtet aus ({0,5},{0,-5}), ein null in einem ausgerichteten Loch wird aufgefüllt, und ein als Ganzes übergebenes params-Array, einstring[]oder ein Collection-Ausdruck eingeschlossen, gilt als seine Werte. Der Provider vonstring.Formatfolgt der Richtlinie, dieToStringhat:CultureInfo.InvariantCultureformatiert invariant, die Muster eines Datums und das generische¤einer Währung eingeschlossen,CultureInfo.CurrentCultureoder ein null wie ein Aufruf ohne Provider, und jeder andere ist EQ2108. DasToString()eines bool istTrueoderFalse. Eine Grenze: Ein float, der in einemobjectgeboxt ist, durch das der Compiler nicht hindurchsehen kann (object o = 0.1f; $"{o}", oder ein zur Laufzeit gebautesobject[]), ist die einfache Zahl darunter. Seit 0.2.0-preview.58. - ✅ Eine Zahl wird über ihren Spezifizierer ausgegeben, wie .NET sie ausgibt:
Eundeschreiben die Ziffern der Mantisse, das Vorzeichen des Exponenten und mindestens drei Exponentenziffern ((12345.0).ToString("E2")ist1.23E+004).F,N,P,C,Gund ein benutzerdefiniertes Muster runden den EXAKTEN Wert der Zahl, den Binärwert eines double eingeschlossen ((0.1).ToString("F20")ist0.10000000000000000555), und jede Ziffer eines long und eines decimal (9007199254740993L.ToString("N0")ist9,007,199,254,740,993), wobei eine exakte Hälfte bei einem double und einem float zur geraden Zahl und bei einem decimal und einer Ganzzahl von null weg gerundet wird ((2.5).ToString("F0")ist2,(2.5m).ToString("F0")ist3,125.ToString("E1")ist1.3E+002).N,F,CundPwerden aus dem eigenenNumberFormatInfoder Kultur gesetzt, das mit jeder Seite reist: ihre Muster, Symbole, Trennzeichen, Gruppengrößen und Standardstellen ((1234.5).ToString("N")ist1,234.500in en-US auf einem Server, dessen .NET ICU liest), in ASCII-Ziffern in jeder Kultur, der von ar-EG eingeschlossen, wie .NET sie schreibt (Seit 0.2.0-preview.61). Eine Kultur, zu der gewechselt wird, ohne dass ein Server gefragt werden kann, wird vonIntl.NumberFormatv3 (ES2023) gesetzt: Chrome und Edge 106, Firefox 116 und Safari 15.4 haben es.XundB(binär) schreiben eine negative Ganzzahl in der Breite ihres Typs (((short)-1).ToString("X")istFFFF, die eines int istFFFFFFFF). Ein benutzerdefiniertes Muster wird gezeichnet, wie .NET es zeichnet: Abschnitte (0.00;(0.00);'zero'), Text in Anführungszeichen und maskierter Text,%und‰, Exponenten (0.00E+00) und die Kommas vor dem Punkt, die durch tausend teilen (#,##0,). Eine Genauigkeit kann 100 Stellen überschreiten, wie .NET es erlaubt. Zahlen werden gruppiert, wie .NET sie gruppiert (es-ES schreibt1.234), und mit dem Minus der Kultur versehen (−von sv-SE), und ein Spezifizierer, den der Typ nicht nimmt, wirft, wie in .NET ((2.5).ToString("D")). Seit 0.2.0-preview.59. Ein double unterD,XoderBwirft, was immer er hält,nintundnuintsind 64-Bit-Ganzzahlen, und das Promillezeichen (؉von ar-SA), die Vorzeichen eines Exponenten und das Prozentzeichen sind die eigenen der Kultur. Seit 0.2.0-preview.61. - ✅
long/ulongsind jetzt exakt: zu JS-BigInt kompiliert, über den Kompatibilitätshelferlong.9007199254740993L + 1List9007199254740994(eine einfache JS-Zahl würde auf…992runden). Literale werden zu BigInt (5L→5n), und ebenso ein Literal ohne Suffix, das nur ein long halten kann (637000000000000000→637000000000000000n, Seit 0.2.0-preview.58); Operanden von Arithmetik/Vergleichen werden inlong()eingehüllt (dasnumber/string→bigintumwandelt), sodass gemischte Ausdrücke nie werfen. Auf der Leitung überqueren 64-Bit-Ganzzahlen als JSON-Strings (Server Actions + SSR-Zustand, überEqJson), sodass Werte jenseits von 2^53 den Hin- und Rückweg überleben. Andere numerische Typsuffixe (1.5f,100u) werden entfernt.
- ✅
-
Threadsicherheit:
- Da JS single-threaded ist, werden
lock-Anweisungen wegkompiliert (ignoriert). -
Thread.Sleepwird nicht unterstützt (verwenden SieTask.Delay).
- Da JS single-threaded ist, werden
Die Treue der Transpilation wird durch ein Konformitäts-Harness (tests/eQuantic.UI.Conformance.Tests) erzwungen:
Jeder Fall führt denselben C#-Ausdruck auf zwei Wegen aus, nach JS transpiliert (über das eingebettete Bun ausgeführt) und
direkt in .NET ausgewertet (Roslyn Scripting), und prüft, dass die Ergebnisse identisch sind. Ein Fall, dessen C# der Build ablehnt (ein EQ-Fehler, etwa EQ2008 für eine Abfrageklausel, die eqc nicht übersetzt), schlägt fehl, bevor eine der beiden Seiten läuft, und nennt jede Ablehnung, es sei denn, er ist geschrieben, um genau diese Ablehnung festzuhalten: Dann prüft er die ID der Diagnose und führt nichts aus. Die Suite zählt ihre Fälle (zum Zeitpunkt des Schreibens über tausend, und mit jedem Konstrukt wachsend); alle sind grün.
(Zuletzt: jedes Dictionary über die Dictionary-Klasse der Runtime, $eq.collections.dictionary.)
Das deckt sowohl Ausdrücke als auch Anweisungsblöcke ab (Kontrollfluss: if/for/foreach/while/switch/
try-catch-finally/lokale Funktionen; der Block läuft in einer IIFE, und sein Rückgabewert wird verglichen).
Jedes Konstrukt wird über einen von drei Mechanismen aufgelöst (siehe docs/DOTNET-COVERAGE-PROGRAM.md):
-
Native Strategie: idiomatisches JS, wenn die Runtime eine Entsprechung hat.
-
.NET-kompatibler Runtime-Helfer: getreue .NET-Semantik, wo JS keine hat. Der Transpiler gibt diese unter einem einzigen Namespace
$eqaus (nach Domäne organisiert), eingebunden mit einem Import pro Modul,import { $eq } from "@equantic/runtime"(aufgelöst durch die Import Map der Seite), statt N loser Helfer-Importe, und$eq.*kann im generierten Gültigkeitsbereich nie mit einem Bezeichner des Benutzers kollidieren:-
$eq.num:dec(exaktes Decimal),decParse/decTryParse/decConvert(ein decimal aus Text oderConvert),intParse/intTryParse/intConvertundrealParse/realTryParse/realConvert(eine Ganzzahl, ein double oder ein float aus Text oderConvert),decFromDouble/decFromSingle(eine Binärzahl in einen decimal),long(Int64 über BigInt),double/single(der Text einer Zahl) -
$eq.math:round(Bankers-Rundung) -
$eq.convert:toBase64String,toBase64CharArray,toHexString,toHexStringLower(die Kodierung eines Arrays, wieConvertvon .NET sie schreibt) -
$eq.text:format(Zahlen-/String-Formatierung),stringBuilder,recordundtuple(der Text eines Records, eines anonymen Objekts und eines Werttupels, wie .NET ihn schreibt) -
$eq.time:dateTime,timeSpan,dateOnly,timeOnly,dateTimeOffset -
$eq.enums:text,parse,tryParse,zero,names,values,isDefined(ein Enum, wie .NET es liest, aus der Form, die eqc an jedem Aufruf schreibt) -
$eq.collections:queue(FIFO),stack(LIFO),dictionary(nach Slot gehalten wie das von .NET, Schlüssel nach Identität oder nach Wert gefunden),linkedList,sortedSet/sortedDictionary/sortedList(nach Schlüssel sortiert) -
$eq.nullable:arith,cmp,unary(gehobeneNullable<T>-Operatoren: null-propagierende Arithmetik und unäre Operatoren, relationale Operatoren, die bei null false liefern) -
$eq.equals: strukturelle (Wert-)Gleichheit für Records/Structs/Tupel (trägt==,.Equals,Contains,Distinct) -
$eq.hash:of(GetHashCode(), das mit$eq.equalsübereinstimmt, sodass Werte, dieEqualsfür gleich hält, gleich hashen, ein Werttyp des Vokabulars wiePointoderTypeStyleeingeschlossen, und ein Typ, der es überschreibt, mit seinem eigenen antwortet),instance(derselbe Aufruf auf einer Referenz, abgelehnt, wenn sie null ist, so wie .NETNullReferenceExceptionwirft, während ein leeresNullable<T>mit 0 antwortet),group(die Methodengruppevalue.GetHashCode, die ihren Empfänger einmal nimmt und einen null-Empfänger dort ablehnt, wo das Delegate erzeugt wird),combine(HashCode.Combine, nach seinen Parametern, auch wenn sie benannt sind) undidentity(object.GetHashCodeeiner Klasse, die es nicht überschreibt, und das eines Arrays, wie auch immer sich seine Elemente ändern). Die eigenen Zahlen von .NET sind über Prozesse hinweg nicht stabil, also hält der Browser den Vertrag ein, nie die Zahl des Servers (Seit 0.2.0-preview.60) -
$eq.css:styleBuilder,classBuilder,joinClasses,whenClass(das Styling-Subsystem)
-
-
Scheitern bei Nicht-Unterstütztem: Ein Konstrukt ohne mögliche JS-Darstellung löst jetzt einen Build-Fehler aus (mit einem stabilen
EQ-Code), statt still wörtlich ausgegeben zu werden. Zwei Ebenen:-
UnsupportedConstructStrategy(EQ2001): Intrinsics für typisierte Referenzen (__makeref,__refvalue,__reftype), Zeigertypen, Funktionszeiger. -
goto/goto case/goto default(EQ2002): keine JS-Entsprechung; strukturieren Sie mit Schleifen/Bedingungen um. (unsafe/fixed/lock-Blöcke werden zu ihrem Rumpf entpackt, wobeilockein single-threaded No-op ist, und ein nacktes Label fällt auf seine innere Anweisung zurück.) -
SemanticValidator, Grenze zwischen Client und Server (EQ21xx): Aufrufe inSystem.IO,System.Net.Http,System.Net.Sockets, EF Core /System.Data, OS-Threading (Thread/Monitor/Mutex),Process,InteropServices(P/Invoke),Reflection.Emitaus einer Client-Komponente. (System.Threading.Tasksist nicht verboten: async wird auf Promise abgebildet.) Die Lösung ist, den Aufruf in eine[ServerAction]zu verschieben.
Jedes andere Konstrukt, das keine Strategie trifft, ist ein Fehler (
EQ1001für einen Ausdruck,EQ1002für eine Anweisung,EQ1003für alles andere), und nichts wird dafür ausgegeben. Früher ging es mit einer Warnung wörtlich durch, was einen erfolgreichen Build und eine Seite bedeutete, die warf — abzulehnen, was er nicht darstellen kann, ist der ganze Sinn. Diagnosen werden in MSBuild-kanonischer Form ausgegeben, sodass siedotnet buildscheitern lassen und die IDE sie verlinkt. Die vollständige Liste steht unter Diagnose. -
| Bereich | Status | Anmerkungen |
|---|---|---|
| Arithmetik / bitweise / Vergleich | ✅ | Ganzzahldivision schneidet ab; %, Verschiebungen, `& |
Math.* / MathF.*
|
✅ | dieselbe Tabelle wie double.* / float.*, mit den eigenen Kompositionen von .NET; Round ist das von .NET (exakter Mittelpunkt, jeder Modus) |
decimal |
✅ | exakt über Decimal (Literale + + - * / % == != < > <= >=, der Rest mit dem Vorzeichen des Dividenden, Seit 0.2.0-preview.58 für %); die Konstanten MaxValue, MinValue, Zero, One und MinusOne (Seit 0.2.0-preview.60); decimal.Parse/TryParse nach der Grammatik und Rundung von .NET, die NumberStyles, die ein Aufruf nennt, eingeschlossen; Convert.ToDecimal und die Casts aus double/float mit den 15 und 7 Stellen von .NET |
| Numerische Konstanten | ✅ |
int.MaxValue, double.Epsilon, … → Literale |
Parsing / Convert.*
|
✅ |
Parse/TryParse auf jedem numerischen Typ, und Convert.ToInt32/ToInt64/ToDouble/… von Text, nach der Grammatik von .NET (Seit 0.2.0-preview.58); bool.Parse/TryParse und Convert.ToBoolean von Text, wie Boolean.TryParse von .NET ihn liest: "True" oder "False" in beliebiger Schreibung, von Leerraum und NULs befreit, und die Ausnahmen von .NET, und Convert.ToBoolean einer Zahl danach, ob sie ungleich null ist, eines bool als er selbst, und eines char oder eines Datums als die InvalidCastException von .NET (Seit 0.2.0-preview.59); Convert.ToString/…; Convert.ToDateTime eines Texts, wie DateTime.Parse ihn in der Kultur liest, die der Aufruf nennt, eines null-Texts als MinValue (Seit 0.2.0-preview.62, wo es mit dem Text antwortete); Convert.ToBase64String, ToBase64CharArray, ToHexString und ToHexStringLower schreiben die Kodierung eines byte[] von .NET, seinen Offset, seine Länge und seine Zeilenumbrüche alle 76 Zeichen eingeschlossen, und lehnen ab, was .NET ablehnt, in dessen Worten; ein Span, der kein Collection-Ausdruck ist, lässt den Build scheitern (EQ1004) (Seit 0.2.0-preview.62, wo jedes das 1,2,255 des Arrays schrieb) |
| Strings | ✅ | Substring/IndexOf/Replace/Split/Pad/Trim/Trim(char)/Concat/Format/Join/IsNullOrEmpty/IsNullOrWhiteSpace; Trim(chars) und seine beiden Hälften lesen den String und dann die Zeichen einmal, ein await darunter eingeschlossen, und keine Zeichen (ein leeres oder ein null-Array) trimmen Leerraum, wie .NET es tut (Seit 0.2.0-preview.60); Trim(), seine beiden Hälften, IsNullOrWhiteSpace und ein nacktes Split() lesen den Leerraum von .NET, wo der von JavaScript U+0085 auslässt und U+FEFF mitnimmt (Seit 0.2.0-preview.58); die eigenen Equals, StartsWith, EndsWith, Contains, IndexOf und LastIndexOf (mit Start und Anzahl) und Replace eines Strings mit einem StringComparison antworten nach diesem Vergleich, ausgeschrieben oder in einer Variable gehalten: OrdinalIgnoreCase behält die Groß-/Kleinschreibung von .NET (das Kelvinzeichen, das lange s und das punktlose i sind nicht k, s und I, und ein Surrogatpaar wird als sein Codepunkt gelesen), ein Start oder eine Anzahl außerhalb des Strings wirft die Ausnahme von .NET, Replace schreibt seine Ersetzung als Text, und CompareTo ordnet nach der aktuellen Kultur, wie string.Compare es tut (Seit 0.2.0-preview.60). IndexOf und LastIndexOf eines Strings ab einem Start, und für eine Anzahl, ohne Vergleich halten bei ihrer Anzahl an, lesen den Start von LastIndexOf als das letzte char, das ein Treffer abdecken darf, und werfen die Ausnahme von .NET für einen Bereich außerhalb des Strings oder einen null-Wert (Seit 0.2.0-preview.62, wo die Anzahl verworfen wurde und LastIndexOf("bc", 4) mit dem Treffer antwortete, der bei 4 beginnt). IndexOf(char, startIndex[, count]) und LastIndexOf(char, startIndex[, count]) durchsuchen diesen Bereich und werfen die Ausnahme von .NET für einen Start oder eine Anzahl außerhalb des Strings: Der Start von LastIndexOf steht auf einem char des Strings, und ein leerer String antwortet mit -1 (Seit 0.2.0-preview.61, wo der Start begrenzt und die Anzahl verworfen wurde). Eine SUCHE nach einem Kulturvergleich, oder mit einer CultureInfo, lässt den Build scheitern (EQ1004), weil der Browser keine kulturabhängige Suche hat: Suchen Sie nach Ordinal oder OrdinalIgnoreCase, oder vergleichen Sie ganze Strings mit Equals oder string.Compare. Die Überladungen ohne Vergleich suchen im Browser ordinal und in .NET nach der aktuellen Kultur (#532). |
char.* |
✅ |
ToUpper/ToLower/IsDigit/IsLetter/IsWhiteSpace/… (Unicode-bewusst; IsWhiteSpace ist die Menge von .NET, U+0085 drin und U+FEFF draußen, Seit 0.2.0-preview.58; ToUpper, ToLower und ihr invariantes Paar ändern die Schreibung eines char wie die eines Strings aus einem Zeichen, in der Kultur, die der Aufruf nennt, und eine null-Kultur wirft die ArgumentNullException von .NET, Seit 0.2.0-preview.62) |
StringBuilder |
✅ | Kompatibilitätstyp: Append(inkl. bool→"True"/"False")/AppendLine("\n")/Insert/Remove/Replace/Clear/Length/ToString. Die Überladungen, die zählen oder einen Bereich nehmen, schreiben, was .NET schreibt, Append('x', 3), Append(text, start, count), Insert(i, text, count), Replace über einen Bereich und ToString(start, length), ein char[] als seine Zeichen und ein null als nichts, und jede Ablehnung ist die von .NET (Seit 0.2.0-preview.61). AppendFormat und AppendJoin hängen an, was string.Format und string.Join schreiben, Capacity folgt den Chunks, die .NET bei jeder Bearbeitung alloziert, MaxCapacity und EnsureCapacity antworten, der Chars-Indexer liest und schreibt ein char, Length kürzt den Text oder füllt ihn mit \0 auf, Equals(StringBuilder) vergleicht den Text und CopyTo kopiert ihn, jeweils mit Ablehnung dessen, was .NET ablehnt; ein interpoliertes Append($"…") oder AppendLine($"…") hängt jeden Teil der Reihe nach an, wie es der Handler von .NET tut, sodass ein Loch, das den Builder liest, die Teile davor sieht, ein benanntes Argument an seinen Parameter gebunden wird und ein Text, der länger ist, als der String des Browsers halten kann, die OutOfMemoryException von .NET ist; ein anderer Provider als die aktuelle Kultur bei Append, GetChunks und den Span-Überladungen lässt den Build scheitern (Seit 0.2.0-preview.61) |
CancellationTokenSource / CancellationToken
|
✅ | die der Runtime, wie die von .NET: eine Quelle (mit einer Verzögerung oder ohne), ihr Token, IsCancellationRequested, Cancel, CancelAfter, Dispose und CreateLinkedTokenSource; None eines Tokens (und default, und new CancellationToken(bool)), IsCancellationRequested, CanBeCanceled, ThrowIfCancellationRequested (eine OperationCanceledException, "The operation was canceled.") und Register eines Callbacks, dessen Registrierung Dispose und Unregister beherrscht. Callbacks laufen einmal, der zuletzt registrierte zuerst, einer, der spät registriert wird, läuft sofort und bekommt die Standardregistrierung zurück (ihr Token ist None), und die, die werfen, werden in einer AggregateException gesammelt, wie in .NET. Eine Methode der drei, als Callback übergeben (token.Register(source.Cancel)), behält ihren Empfänger, und eine verknüpfte Quelle lässt, wenn sie freigegeben wird, die Tokens los, denen sie folgt. Eine freigegebene Quelle lässt auch ihre Callbacks los, wobei sich eine Registrierung danach noch einmal abmeldet, und new CancellationTokenSource(delay, timeProvider) lässt den Build mit EQ2004 scheitern: Die Quelle der Runtime wird von der echten Uhr getaktet. Eine Verzögerung wird bis 4294967294 ms angenommen, wie .NET sie annimmt, und ein Monat wird abgewartet, obwohl der Timer eines Browsers etwa 24,8 Tage hält. default(CancellationTokenRegistration) ist die Registrierung von nichts, deren Token None ist. Jedes andere Mitglied der drei lässt den Build mit EQ2004 scheitern (Seit 0.2.0-preview.61) |
| LINQ | ✅ | Where/Select/SelectMany/Where-Select(indiziert)/OrderBy/Distinct(By)/GroupBy/ToDictionary/ToLookup/Zip/Chunk/MinBy/MaxBy/Take(While)/Skip(While)/Aggregate/Sum/Min/Max/Average/Count/Any/All/First/Last/Concat/Reverse/Join/GroupJoin/ThenBy/ThenByDescending (Join/GroupJoin = reihenfolgeerhaltender Hash-Join über primitiven Schlüsseln; OrderBy+ThenBy = eine einzige stabile zusammengesetzte Sortierung, Quelle kopiert). IGrouping aus GroupBy/ToLookup ist als Sequenz verwendbar (iterieren, g.Select/g.Sum/g.Count()) und stellt g.Key bereit; der [key]-Indexer von ILookup ist nicht modelliert. Ein Vergleicher, der den Standard verlangt, passiert ToDictionary, ToLookup, GroupBy, Distinct und ToHashSet, und jeder andere lässt den Build scheitern (EQ2007) (Seit 0.2.0-preview.61). |
| Collections | ✅ | List, Dictionary, HashSet (inkl. Initialisierern und .Count; eine Menge ist die Menge der Runtime, nach Slot gehalten und ihre Elemente nach Wert findend, wo ihr Elementtyp es tut, Seit 0.2.0-preview.61); Queue/Stack/LinkedList als Kompatibilitätstypen; sortierte Familie SortedSet/SortedDictionary/SortedList (nach Schlüssel sortierte Aufzählung). Jedes Dictionary ist die Dictionary-Klasse der Runtime, $eq.collections.dictionary, die nach Slot aufzählt wie die von .NET, ein Record-/Struct-/Tupel-Schlüssel nach Wert gefunden (Konstruktion und Kopie, d[k] get/set, ContainsKey/Add/Remove/Clear/TryGetValue/GetValueOrDefault/TryAdd/ContainsValue, Keys/Values/Count, foreach) (Seit 0.2.0-preview.60). Das Lesen eines fehlenden Schlüssels wirft KeyNotFoundException, und ebenso eine Verbundzuweisung und ein Inkrement auf einem solchen (Seit 0.2.0-preview.58 für das Inkrement). Der Indexer ILookup[key] gibt die Gruppe zurück (oder eine leere für einen fehlenden Schlüssel). Add und Clear von ICollection<T> erreichen die Collection hinter der Schnittstelle, und IList<T> und IReadOnlyList<T> lesen und schreiben einen eigenen Typ über seinen Indexer und zählen ihn über sein Count (Seit 0.2.0-preview.61). |
enum |
✅ | String des Mitgliedsnamens, ein [Flags]-Enum seine Zahl (Gleichheit/switch/ternärer Operator, Text und Formate, die statischen Mitglieder von Enum) |
long/ulong
|
✅ | exakt über BigInt (long-Helfer); Literale 5L→5n, und Konstanten ebenso (TimeSpan.TicksPerSecond → 10000000n, Seit 0.2.0-preview.60), auf der Leitung als JSON-String |
DateTime |
✅ | tickgenauer Kompatibilitätstyp DateTime: Konstruktoren, Komponenten, Add* auf den Tick genau, wie .NET 7+ darauf landet (ein Bruchteil abgeschnitten, eine Anzahl oder ein Ergebnis außerhalb des Bereichs in den Worten von .NET abgelehnt, AddMicroseconds eingeschlossen, Seit 0.2.0-preview.59), -→TimeSpan, Vergleiche, .ToString()/format; ISO-8601 auf der Leitung + Hydration. ToString(), ToString(format) und ToString(format, provider) geben aus, wie .NET sie ausgibt (kein Spezifizierer, oder ein null- oder leerer, ist das G der aktuellen Kultur, und keine Zeitzone verschiebt die Teile des Werts außer bei U): jeder einbuchstabige Standardspezifizierer (d bis Y, s, u und R aus den eigenen Teilen des Werts, o aus ihnen mit der Zone, die seine Art nennt, U nach UTC verschoben, sofern er nicht schon UTC ist), ein Buchstabe, der kein Standardspezifizierer ist, abgelehnt, wie .NET ihn ablehnt, ein benutzerdefiniertes Muster Token für Token mit seinem Sekundenbruchteil (HH:mm:ss.fff), die Trennzeichen der Kultur für / und : und die Ära für g (Seit 0.2.0-preview.61), die Muster der invarianten Kultur für CultureInfo.InvariantCulture oder wenn keine Kultur gilt, und eine benannte Kultur beim Build abgelehnt (EQ2108); ToShortDateString() und seine Geschwister sind d, D, t und T (Seit 0.2.0-preview.60). Add, Subtract, AddMonths, AddYears und die Operatoren + und -, Verbundformen eingeschlossen, lehnen ein Ergebnis außerhalb des Kalenders in den Worten von .NET ab, wobei die Operatoren ihren Parameter t nennen (Seit 0.2.0-preview.60). Jeder Konstruktor, den der Compiler durchlässt, baut den Wert, den .NET baut, jedes Argument am Platz seines Parameters, ein benanntes eingeschlossen: die Komponenten mit einer Millisekunde, einer Mikrosekunde und einer Art, die Ticks mit einer Art, und ein DateOnly mit einem TimeOnly, wobei eine Komponente, eine Art oder Ticks außerhalb des Bereichs in den Worten von .NET abgelehnt werden und eine Überladung, die einen Calendar nimmt, beim Build abgelehnt wird (EQ1004). Ein Wert trägt sein Kind (Now und Today lokal, UtcNow UTC, SpecifyKind), das die Arithmetik behält und das Gleichheit, Ordnung und der Hash auslassen; ToLocalTime() und ToUniversalTime() konvertieren danach in der Zeitzone des Browsers, Microsecond und Nanosecond antworten, und das JSON schreibt einen UTC-Wert mit Z und einen lokalen mit seinem Offset und liest sie zurück, wie System.Text.Json es tut (Z eine UTC-Zeit, ein Offset in die lokale Zeit des Browsers verschoben), während DateTime.Parse eine geschriebene Zone in die lokale Zeit verschiebt. Eine lokale Zeit, die aus einem Zeitpunkt gemacht wurde, behält, welche von zwei wiederholten Stunden sie ist, wie .NET es neben der Art behält, durch die Arithmetik hindurch bis SpecifyKind, sodass ihr Weg zurück nach UTC dieser Zeitpunkt ist, und der Text eines Datums schreibt seine Art: o und K beenden eine UTC-Zeit mit Z und eine lokale mit ihrem Offset, und z, zz und zzz schreiben den Offset der Zone für alles, was nicht UTC ist (Seit 0.2.0-preview.61) |
TimeSpan |
✅ | tickgenauer Kompatibilitätstyp TimeSpan: From*, Konstruktoren, Komponenten/Gesamtwerte, + -, Vergleiche, .ToString() im .NET-Format "c" und seine Standard- und benutzerdefinierten Formate (Seit 0.2.0-preview.62); Parse und TryParse lesen, wie .NET ohne Format liest, jeder Teil gegen seine Grenze (Seit 0.2.0-preview.62); "c" auf der Leitung + Hydration |
DateOnly / TimeOnly
|
✅ | Kompatibilitätstypen (.NET 6+): Konstruktoren, Komponenten, Add*(+ Umlauf bei TimeOnly, ein Produkt konvertiert, wie .NET 9+ einen double konvertiert, Seit 0.2.0-preview.59, ein DateOnly, der den Kalender verlässt, in den Worten von .NET abgelehnt, Seit 0.2.0-preview.60), Vergleiche, ToString(), ToString(format) und ToString(format, provider), wie .NET sie in der geltenden Kultur ausgibt: d und t ohne Format, die Standardspezifizierer, die jeder Typ nimmt, und ein benutzerdefiniertes Muster seiner eigenen Teile, alles andere die FormatException von .NET, und ToShortDateString(), ToLongDateString(), ToShortTimeString() und ToLongTimeString() als d, D, t und T (Seit 0.2.0-preview.61); ISO auf der Leitung + Hydration |
DateTimeOffset |
✅ | tickgenauer Kompatibilitätstyp (Wanduhrzeit + Offset, nach dem Zeitpunkt verglichen): Konstruktoren, Komponenten, MinValue/MaxValue (Seit 0.2.0-preview.58), Offset/UtcDateTime/LocalDateTime, ToOffset, Add* auf den Tick genau mit AddMilliseconds/AddMicroseconds, wobei die Uhrzeit und dann die UTC-Zeit in den Worten von .NET abgelehnt werden (Seit 0.2.0-preview.59), AddMonths und AddYears eingeschlossen (Seit 0.2.0-preview.60), From/ToUnixTime* (ToUnixTimeSeconds und ToUnixTimeMilliseconds ein long, vor 1970 abgerundet, wie .NET es rundet, Seit 0.2.0-preview.61), - →TimeSpan, Vergleiche nach Zeitpunkt, ToString() und seine Überladungen, wie .NET sie in der geltenden Kultur ausgibt: das kurze Datum, die lange Uhrzeit und der Offset ohne Format, jeder Standardspezifizierer außer U, wobei o und K den Offset schreiben und u und R danach konvertieren (Seit 0.2.0-preview.61); ISO+Offset auf der Leitung + Hydration. Jeder Konstruktor baut den Wert, den .NET baut: die Komponenten mit einer Millisekunde und einer Mikrosekunde, die Ticks, ein DateOnly mit einem TimeOnly, und ein DateTime, das ohne Offset den des Browsers für eine Zeit nimmt, die nicht UTC ist; ein Offset, der keine ganzen Minuten hat, vierzehn Stunden überschreitet oder für ein UTC-DateTime nicht null ist, wird in den Worten von .NET abgelehnt. Now ist der Zeitpunkt beim Offset des Browsers, LocalDateTime und ToLocalTime() lesen die Zeitzone des Browsers, und Microsecond und Nanosecond antworten (Seit 0.2.0-preview.61) |
record / struct / Werttupel |
✅ |
Wertsemantik. Records/Structs sind Instanzen ihrer Zwillingsklassen (das positionale new Point(1,2) ruft den Konstruktor auf, den C# aufruft, und ein Objektinitialisierer wird danach angewendet), Tupel sind Arrays mit Elementzugriff nach Position (t.Item1) und nach deklariertem Namen ((int X, int Y).X) → Index. ==/!=, .Equals, Distinct vergleichen strukturell über $eq.equals, und Contains, IndexOf und eine Menge nach dem Standardvergleicher des Elementtyps, das Array-Mitglied eines Tupels nach Referenz (Seit 0.2.0-preview.61); with kopiert und ersetzt. Ein veränderliches Struct und ein Werttupel werden dort kopiert, wo C# sie kopiert, sodass ein Schreiben über einen Namen nie durch einen anderen sichtbar wird, dem der Wert zugewiesen, übergeben, zurückgegeben oder in den er geboxt wurde: Der Zwilling kopiert beim Schreiben und kopiert this, wo es das Mitglied seines Structs verlässt (Seit 0.2.0-preview.61). Die Dekonstruktion var (a, b) = … funktioniert für Tupel (Array-Destrukturierung, Verwerfungslöcher) und Records (Objekt-Destrukturierung in Deconstruct-Reihenfolge). Records werden als benannte JS-Klassen ausgegeben, die ihre Instanzmethoden des Benutzers + ein strukturelles equals, ein prototyperhaltendes with und ein toString wie in .NET tragen; die Wertsemantik ist unverändert. Das equals eines Records vergleicht seinen Laufzeittyp (ein Dog ist nie gleich einem Animal), was seine Basis vergleicht, und jedes Feld, das er deklariert, ein privates eingeschlossen, und sein Text nennt die Mitglieder, die PrintMembers von .NET schreibt, in dessen Reihenfolge: zuerst die Mitglieder seiner Basis, dann seine eigenen, eine berechnete Eigenschaft und eine Eigenschaft, deren Getter eine eigene Zugriffsebene hat, eingeschlossen, eine Überschreibung der Basis überlassen, jedes einmal, und Name { } für keines. Zwei Mitglieder eines Records oder eines Structs, die auf einem Namen des Zwillings landen (struct S(int x) { public int X … }, ein Feld celsius neben Celsius), lassen den Build scheitern (EQ1007); der Parameter eines primären Konstruktors, der nur von einem Initialisierer gelesen wird (struct Point(int x) { public int X { get; } = x; }), gehört dem Konstruktor und kompiliert; eine abstrakte Eigenschaft hält nichts. Jeder Wert wird geschrieben, wie .NET ihn schreibt: ein bool als True, ein Enum über den Namen seines Mitglieds, ein null als nichts und eine Zahl in der geltenden Kultur, und eine einfache Klasse oder ein einfaches Struct, die keinen eigenen Text schreiben, schreiben den vollständigen Namen ihres Typs (My.App.Outer+In), wo der Browser [object Object] oder den Text eines Records schrieb; eine generische tut es noch nicht (Seit 0.2.0-preview.61). Ein positionaler Parameter, dessen Eigenschaft der Record deklariert (record Box(int X) { public int X { get; set; } = X; }), ist ein Mitglied, das deklarierte (Seit 0.2.0-preview.61). Ein Mitglied beginnt so, wie seine Deklaration es sagt, was immer der Ausdruck ist: ein positionaler Parameter, der Initialisierer einer Eigenschaft oder eines Felds (= 1.5m, = 5 auf einem long, = new(), = "#" + Id), einmal geschrieben, im Konstruktor der Klasse (Seit 0.2.0-preview.59), der bei jeder Konstruktion jeden Initialisierer ausführt, was immer ein Objektinitialisierer danach setzt (seit 0.2.0-preview.61; siehe Konstruktoren oben). Die Mitglieder eines Records und eines Structs werden abgesenkt wie die einer Klasse: Eine async-Methode, ein Iterator, ein out- oder ref-Parameter und die Variable, die das Muster eines Ausdrucksrumpfs bindet (o is P p && p.X == X), funktionieren in ihren Methoden, Operatoren, Konversionen und berechneten Eigenschaften, ihren eigenen und einem Standard, den eine Schnittstelle liefert, und ein Getter, der per yield liefert, gibt seine Sequenz auch in einer Klasse zurück (Seit 0.2.0-preview.60). Die Build-Pipeline entdeckt Records durch Scannen und gibt jeden als eigenes Modul aus; Komponenten, die einen Record referenzieren, importieren ihn automatisch (reaktiv, keine fest codierte Liste). SSR-Werte werden auf dem Client zurück in die Record-Klasse rehydriert (rekursiv, wobei verschachtelte Records und Kompatibilitätsmitglieder wiederhergestellt werden), sodass Methoden/instanceof überleben. Deckt positionale und Rumpf-Records und einfache Structs ab, dazu Record-Vererbung (extends + super) und generische Records, deren Gleichheit den geschlossenen Typ vergleicht, als den ein Wert gebaut wurde: Ein Box<int> ist nie gleich einem Box<double>, und einer, der in generischem Code gebaut wurde, dessen Typargumente der Build nicht kennen kann, wird nicht für einen anderen Typ gehalten (Seit 0.2.0-preview.61). Dictionaries mit Record-Schlüssel (Dictionary<RecordKey, V>) finden einen Schlüssel nach Wert, sodass wertgleiche Schlüssel kollidieren wie in .NET. Jedes Mitglied vergleicht, wie EqualityComparer<T>.Default seinen Typ vergleicht, also ein Array, eine Liste oder die Schnittstelle einer Collection (IReadOnlyList<int>) nach Referenz, und das Equals eines Tupels vergleicht seine Elemente auf dieselbe Weise, ein Tupel anderer Stelligkeit ungleich und ein null-Paar nur einem anderen gleich; ein Tupel, das nur als object gesehen wird, oder als eine Schnittstelle, die ein Tupel implementiert, ist im Web ein Array und vergleicht nach seinen Elementen (Seit 0.2.0-preview.61). Der Text eines Werttupels ist der von .NET, (1, a): jedes Element, wie sein Typ es in der geltenden Kultur schreibt, ein verschachteltes Tupel und ein anonymes Objekt eingeschlossen, ein null-Element als nichts, und acht Elemente oder mehr flach, über ToString() und seine Methodengruppe, ein Interpolationsloch (auch eines mit Ausrichtung oder Format, das ein Tupel ignoriert), eine Verkettung, string.Concat, string.Join, string.Format, einen StringBuilder, Convert.ToString und das Mitglied eines Records, die Paare von Zip darunter, wo der Browser 1,2 schrieb. Ein Element, das ein Array, eine Liste, ein Dictionary, ein Set oder ein Delegate ist, schreibt den Namen seines Typs, System.Int32[], wie .NET, und der Wert selbst ebenso, wo immer er in Text geschrieben wird, wo der Browser 1,2 oder den Quelltext einer Funktion schrieb. Equals(object) findet ein Tupel anderer Elementtypen ungleich, ((byte)1, 2).Equals((object)(1, 2)) ist false, wo der Browser dasselbe Array sah. Die Methodengruppe t.Equals vergleicht wie der Aufruf, und das Equals eines Nullable-Tupels ist das seines Werts (Seit 0.2.0-preview.64). Ein als object gehaltenes Tupel, dessen Typ der Build nicht sieht, schreibt weiterhin 1,2 und vergleicht nach seinen Elementen, ebenso eines, das dem Delegate der Methodengruppe Equals übergeben wird. |
Anonymes Objekt (new { A = 1 }) |
✅ | Ein einfaches Objekt, seine Mitglieder unter den Namen seines Zwillings ({ a: 1 }). Equals, als Aufruf oder als Methodengruppe, vergleicht wie das Equals, das C# für einen anonymen Typ schreibt: jedes Mitglied nach der Gleichheit seines Typs (ein Array oder eine Liste nach Referenz, ein decimal und ein Tupel nach Wert), und nur ein anderes anonymes Objekt mit denselben Mitgliedern in derselben Reihenfolge kann gleich sein, also sind andere Mitglieder, dieselben Mitglieder in anderer Reihenfolge und ein Record mit denselben Mitgliedern es nicht. Der Aufruf warf vorher. Mitglieder anderer Typen ergeben ebenfalls einen anderen Typ: new { A = (byte)1 }.Equals(new { A = 1 }) ist false. Über null werfen Equals, ToString und das Erzeugen des Delegates einer der beiden Methodengruppen NullReferenceException, wie in .NET, während ein Interpolationsloch und eine Verkettung nichts schreiben. == bleibt die Referenzgleichheit, und GetHashCode gibt gleichen Objekten denselben Hash, und ein Set, Distinct und GroupBy nehmen gleiche Objekte als einen Schlüssel, wobei Distinct das erste jedes behält. Sein Text ist der von .NET, { R = 255, Name = a } und { }: die C#-Namen der Mitglieder in ihrer Reihenfolge, jedes Mitglied, wie sein Typ es in der geltenden Kultur schreibt, über alle Wege, die der Text eines Tupels nimmt, wo der Browser [object Object] schrieb (Seit 0.2.0-preview.64). Ein als object gehaltenes, oder dem Delegate der Methodengruppe Equals übergebenes, schreibt weiterhin [object Object] und vergleicht nach seinen Mitgliedern, welche Typen sie auch haben. |
Nullable<T> (T?) |
✅ |
HasValue/Value, GetValueOrDefault() (typbewusster Standardwert: 0/false/$eq.num.dec(0)/Null-Mitglied des Enums/…) und GetValueOrDefault(fallback), ??; gehobene Operatoren über $eq.nullable.*: Arithmetik propagiert null, Vergleiche (< > <= >=) sind false, wenn eine der Seiten null ist (keine numerische Umwandlung); eine Verbundzuweisung (+=, *=, …), ein Inkrement und ein unäres -, ~ oder + auf einer nullbaren Zahl sind ebenfalls gehoben, wobei ein null null bleibt und ein Wert der Regel seines Typs folgt (ein float? rundet, ein byte? läuft um, ein decimal? bleibt exakt; Seit 0.2.0-preview.58). GetValueOrDefault() ohne Argument auf DateTime?/Guid?/Struct liefert null, verwenden Sie dort also die Form mit Fallback. |
Guid |
✅ |
Guid.NewGuid()→crypto.randomUUID(), Guid.Empty, Guid.Parse/TryParse/new Guid(text) → der kanonische kleingeschriebene D-Text (Seit 0.2.0-preview.60); als String auf der Leitung |
🏁 Hier starten
🏗️ Architektur
- Architekturüberblick
- Write-once-Komponenten
- Deklarative Oberfläche
- Paketarchitektur
- Komponenten
- Styling
- Lokalisierung
- Analytics und GTM
📱 Write-once
- Photon-Engine
- Design System
- Fähigkeiten
- Speicher
- Formulare
- Code-Editor
- Markdown
- Mermaid
- E-Mail-Rendering
⚙️ Kompilierung
⚡ Runtime
🔌 Server
🎨 Ökosystem
🚀 Entwicklung