Ich versuche einen Typ zu erstellen, der dem von Rust Result
oder Haskell ähnelt, Either
und ich bin so weit gekommen:
public struct Result<TResult, TError>
where TResult : notnull
where TError : notnull
{
private readonly OneOf<TResult, TError> Value;
public Result(TResult result) => Value = result;
public Result(TError error) => Value = error;
public static implicit operator Result<TResult, TError>(TResult result)
=> new Result<TResult, TError>(result);
public static implicit operator Result<TResult, TError>(TError error)
=> new Result<TResult, TError>(error);
public void Deconstruct(out TResult? result, out TError? error)
{
result = (Value.IsT0) ? Value.AsT0 : (TResult?)null;
error = (Value.IsT1) ? Value.AsT1 : (TError?)null;
}
}
Angesichts der Tatsache, dass beide Typparameter beschränkt sind notnull
, warum beschwert es sich (überall dort, wo ein Typparameter mit dem Nullzeichen ?
dahinter steht), dass:
Ein nullfähiger Typparameter muss als Werttyp oder nicht nullbarer Referenztyp bekannt sein. Erwägen Sie das Hinzufügen einer 'Klasse', 'Struktur' oder Typeinschränkung.
?
Ich verwende C # 8 unter .NET Core 3 mit aktivierten nullfähigen Referenztypen.
c#
generics
type-constraints
c#-8.0
nullable-reference-types
Schuh Diamente
quelle
quelle
Antworten:
Grundsätzlich fragen Sie nach etwas, das in IL nicht dargestellt werden kann. Nullable-Werttypen und nullable-Referenztypen sind sehr unterschiedliche Bestien, und obwohl sie im Quellcode ähnlich aussehen, ist die IL sehr unterschiedlich. Die Nullable - Version eines Werttyp
T
ist eine andere Art (Nullable<T>
) , während die Nullable - Version eines ReferenztypT
ist der gleiche Typ, mit Attributen , den Compiler zu sagen , was zu erwarten ist .Betrachten Sie dieses einfachere Beispiel:
Das ist aus dem gleichen Grund ungültig.
Wenn wir uns darauf beschränken
T
, eine Struktur zu sein, hat die für dieGetNullValue
Methode generierte IL den RückgabetypNullable<T>
.Wenn wir uns darauf beschränken
T
, ein nicht nullfähiger Referenztyp zu sein, hat die für dieGetNullValue
Methode generierte IL den RückgabetypT
, jedoch mit einem Attribut für den Nullwertaspekt.Der Compiler kann keine IL für eine Methode generieren, die einen Rückgabetyp von beiden
T
undNullable<T>
gleichzeitig hat.Dies ist im Grunde das Ergebnis von nullbaren Referenztypen, die überhaupt kein CLR-Konzept sind. Es ist nur Compiler-Magie, die Ihnen hilft, Absichten im Code auszudrücken und den Compiler dazu zu bringen, zur Kompilierungszeit einige Überprüfungen durchzuführen.
Die Fehlermeldung ist jedoch nicht so klar wie sie sein könnte.
T
ist als "Werttyp oder nicht nullbarer Referenztyp" bekannt. Eine genauere (aber deutlich wortreichere) Fehlermeldung wäre:Zu diesem Zeitpunkt würde der Fehler vernünftigerweise auf unseren Code zutreffen - der Typparameter ist nicht "als Werttyp bekannt" und nicht als "nicht nullbarer Referenztyp" bekannt. Es ist bekannt, dass es einer der beiden ist, aber der Compiler muss wissen, welcher .
quelle
Nullable<T>
ist ein besonderer Typ, den du nicht selbst machen kannst. Und dann gibt es noch den Bonuspunkt, wie Boxen mit nulllable Typen gemacht wird.Der Grund für die Warnung wird im Abschnitt
The issue with T?
von Try out Nullable Referenztypen . Lange Rede kurzer Sinn, wenn Sie verwendenT?
, müssen Sie angeben, ob der Typ eine Klasse oder eine Struktur ist. Möglicherweise erstellen Sie für jeden Fall zwei Typen.Das tiefere Problem besteht darin, dass die Verwendung eines Typs zum Implementieren des Ergebnisses und zum Halten der Werte für Erfolg und Fehler dieselben Probleme zurückbringt, die das Ergebnis beheben sollte, und einige weitere.
Ergebnis (und entweder) in F #
Der Ausgangspunkt sollte der Ergebnistyp von F # und diskriminierte Gewerkschaften sein. Immerhin funktioniert das schon in .NET.
Ein Ergebnistyp in F # ist:
Die Typen selbst tragen nur das, was sie brauchen.
DUs in F # ermöglichen einen umfassenden Mustervergleich, ohne dass Nullen erforderlich sind:
Emulieren Sie dies in C # 8
Leider hat C # 8 noch keine DUs, sie sind für C # 9 geplant. In C # 8 können wir dies emulieren, aber wir verlieren die erschöpfende Übereinstimmung:
Und benutze es:
Ohne erschöpfenden Mustervergleich müssen wir diese Standardklausel hinzufügen, um Compiler-Warnungen zu vermeiden.
Ich bin immer noch auf der Suche nach einer Möglichkeit, eine vollständige Übereinstimmung zu erzielen, ohne tote Werte einzuführen, auch wenn diese nur eine Option sind.
Option / Vielleicht
Das Erstellen einer Optionsklasse mithilfe eines umfassenden Abgleichs ist einfacher:
Welches kann verwendet werden mit:
quelle