J'essaie de créer un type similaire à celui de Rust Result
ou Haskell Either
et je suis arrivé jusqu'ici:
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;
}
}
Étant donné que les deux types de paramètres sont restreints notnull
, pourquoi se plaint-il (partout où il y a un paramètre de type avec le ?
signe nullable après) que:
Un paramètre de type nullable doit être connu pour être un type de valeur ou un type de référence non nullable. Pensez à ajouter une contrainte «classe», «struct» ou type.
?
J'utilise C # 8 sur .NET Core 3 avec les types de référence nullables activés.
c#
generics
type-constraints
c#-8.0
nullable-reference-types
Chaussure Diamente
la source
la source
Réponses:
Fondamentalement, vous demandez quelque chose qui ne peut pas être représenté en IL. Les types de valeur Nullable et les types de référence Nullable sont des bêtes très différentes, et bien qu'ils semblent similaires dans le code source, l'IL est très différent. La version nullable d'un type de valeur
T
est un type différent (Nullable<T>
) tandis que la version nullable d'un type de référenceT
est du même type, avec des attributs indiquant au compilateur à quoi s'attendre.Considérez cet exemple plus simple:
C'est invalide pour la même raison.
Si nous contraignons
T
à être une structure, alors l'IL généré pour laGetNullValue
méthode aurait un type de retour deNullable<T>
.Si nous contraignons
T
à être un type de référence non nullable, alors l'IL généré pour laGetNullValue
méthode aurait un type de retour deT
, mais avec un attribut pour l'aspect nullabilité.Le compilateur ne peut pas générer IL pour une méthode qui a un type de retour à la fois
T
etNullable<T>
en même temps.C'est essentiellement le résultat du fait que les types de référence nullables ne sont pas du tout un concept CLR - c'est juste la magie du compilateur pour vous aider à exprimer vos intentions dans le code et faire en sorte que le compilateur effectue une vérification au moment de la compilation.
Le message d'erreur n'est cependant pas aussi clair qu'il pourrait l'être.
T
est connu pour être "un type de valeur ou un type de référence non nullable". Un message d'erreur plus précis (mais beaucoup plus verbeux) serait:À ce stade, l'erreur s'appliquerait raisonnablement à notre code - le paramètre type n'est pas "connu pour être un type de valeur" et il n'est pas "connu pour être un type de référence non nullable". Il est connu pour être l'un des deux, mais le compilateur doit savoir lequel .
la source
Nullable<T>
est un type spécial que vous ne pouvez pas fabriquer vous-même. Et puis il y a le point bonus de la façon dont la boxe se fait avec des types nulles.La raison de l'avertissement est expliqué dans la section
The issue with T?
de Expérimentaux Nullable Types de référence . Pour faire court, si vous utilisez,T?
vous devez spécifier si le type est une classe ou une structure. Vous pouvez finir par créer deux types pour chaque cas.Le problème le plus profond est que l'utilisation d'un type pour implémenter Result et conserver les valeurs Success et Error ramène les mêmes problèmes que Result devait résoudre, et quelques autres.
Résultat (et l'un ou l'autre) en F #
Le point de départ doit être le type de résultat de F # et les unions discriminées. Après tout, cela fonctionne déjà sur .NET.
Un type de résultat en F # est:
Les types eux-mêmes ne portent que ce dont ils ont besoin.
Les DU en F # permettent une correspondance exhaustive des motifs sans nécessiter de valeurs nulles:
Émuler cela en C # 8
Malheureusement, C # 8 n'a pas encore de DU, ils sont prévus pour C # 9. En C # 8, nous pouvons émuler cela, mais nous perdons la correspondance exhaustive:
Et utilisez-le:
Sans correspondance de modèle exhaustive, nous devons ajouter cette clause par défaut pour éviter les avertissements du compilateur.
Je cherche toujours un moyen d'obtenir une correspondance exhaustive sans introduire de valeurs mortes, même si elles ne sont qu'une option.
Option / Peut-être
La création d'une classe Option par la manière qui utilise une correspondance exhaustive est plus simple:
Qui peut être utilisé avec:
la source