Échec de la conversion implicite avec un type générique pour un type d'interface
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
Console.WriteLine(e.GetType().Name);
return e;
}
Violon (ne pas utiliser): https://dotnetfiddle.net/NxAw9l
Violon mis à jour: https://dotnetfiddle.net/NrARTl
Un type générique échoue pour la conversion implicite au code ci-dessus. Voir les Ok()et les NotOk()appels de fonction et les types de retour. Un type générique complexe échoue et je ne comprends pas pourquoi. J'ai simplifié cela à partir d'une fonction d'un type de retour de IEnumerable<IEnumerable<T>>. Cela IEnumerable<T>échoue toujours. Je pense que si je peux comprendre pourquoi cela échoue, je vais aussi résoudre le vrai, je suppose. Merci pour votre aide et votre temps.
Voici le message d'erreur si vous souhaitez:
Error CS0029 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<byte>' to 'Maybe<System.Collections.Generic.IEnumerable<byte>>'
Mise à jour: Renvoyer Byte [] depuis NotOK () ne peut pas fonctionner car dans mon code source réel, j'ai une requête LINQ que je dois dépendre de son exécution différée paresseuse (c'est-à-dire qu'il doit renvoyer strictement IEnumerable) (voir la même réponse =>https://stackoverflow.com/a/63880804/5917087).
Réponses
La norme C # n'autorise actuellement pas les conversions implicites depuis ou vers les interfaces.
Il s'agit d'un problème bien connu lors de l'implémentation d'un type Maybe<T>(ou Optional<T>, comme on l'appelle souvent) en C #. Il y a une discussion en cours à ce sujet sur le forum github du langage C #:
- https://github.com/dotnet/roslyn/issues/14186
Pour contourner le problème, vous pouvez créer le Maybe<T>constructeur internalet ajouter une classe d'assistance statique non générique:
private static class Maybe
{
public static Maybe<T> From<T>(T value) =>
value == null ? new Maybe<T>() : new Maybe<T>(value);
}
qui vous permet d'utiliser l'inférence de type et l'écriture Maybe.From(a), qui est un peu plus courte que new Maybe<IEnumerable<byte>>(a).
Je vais prolonger la réponse de @ Heinzi:
Vous pouvez également utiliser des méthodes d'extension:
static class MaybeExtensions
{
public static Maybe<T> AsMaybe<T>(this T value)
{
return new Maybe<T>(value);
}
public static Maybe<TResult> AsMaybe<T, TResult>(this T value)
where T : unmanaged
where TResult : unmanaged
{
return new Maybe<TResult>(Unsafe.As<T, TResult>(ref value));
}
}
Et dans vos méthodes d'appel, vous pouvez les utiliser comme:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
return e.AsMaybe();
}
private static Maybe<byte> OK()
{
return 5.AsMaybe<int, byte>();
}
// Alternatively
private static Maybe<byte> OK()
{
return ((byte)5).AsMaybe();
}
Vous avez besoin de la AsMaybe<T, TResult>surcharge pour les types de valeur qui peuvent se convertir les uns aux autres. Par exemple, lorsque vous effectuez 5.AsMaybe()un retour Maybe<int>, si le type de retour de votre méthode est, Maybe<byte>vous devrez convertir Maybe<int>en Maybe<byte>, et la surcharge le fait pour vous.
Désormais, l'opérateur de conversion de type dans Maybe<T>devient redondant. Et vous pouvez utiliser à la varplace du nom de type complet:
Maybe<int> obj1 = 5; // use operator
var obj2 = 5.AsMaybe(); // use extension method
Vous ne pouvez pas définir une conversion vers / à partir d'un type d'interface, si vous changez votre exemple pour utiliser List<T>plutôt IEnumerable<T>qu'il compilera -https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#user-defined-conversions
Change ça :
private static Maybe<IEnumerable<byte>> NotOK()
{
IEnumerable<byte> a = new byte[] { 1, 2 };
return a;
}
dans ceci:
private static Maybe<IEnumerable<byte>> NotOK()
{
var a = new byte[] { 1, 2 };
return a;
}
La structure:
private struct Maybe<T>
{
private readonly T value;
private readonly bool hasValue;
private Maybe(T value)
{
this.value = value;
hasValue = true;
}
public static implicit operator Maybe<T>(T value)
{
return value == null ? new Maybe<T>() : new Maybe<T>(value);
}
public bool HasValue(){
return this.hasValue;
}
public T GetValue(){
return this.value;
}
}
private static Maybe<byte> OK()
{
return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
Byte[] a = new byte[] { 1, 2 };
Console.WriteLine(a.GetType().Name);
return a;
}
Utilisation:
public static void Main(string[] args){
var t1 = OK();
var t2 = NotOK();
Console.WriteLine("t1 type is " + t1.GetType().Name);
Console.WriteLine("t2 type is " + t2.GetType().Name);
if(t2.HasValue())
{
List<byte> search = t2.GetValue().Where(b => b > 0).ToList();
foreach(byte num in search){
Console.WriteLine(num);
}
}
}
La référence IEnumerable<byte> ane change pas le type, vous pouvez continuer vers varou byte[]et la requête avec LINQ, après, voir dans l'exemple complet
Voir l'exemple complet: https://dotnetfiddle.net/V8RHQe
IEnumerable est une interface. Le compilateur ne sait pas avec quel type travailler. Mettez ToList()à la fin de votre sélection comme suit:
private static Maybe<IEnumerable<byte>> NotOK()
{
var e = new[] { 1, 2, 3 }.Select(x => (byte)x).ToList();
Console.WriteLine(e.GetType().Name);
return e;
}
Pour comprendre ce qui se passe, essayez de créer une méthode comme follow dans votre classe et regardez le compilateur pleurer :)
public static implicit operator Maybe<IEnumerable<T>>(IEnumerable<T> value)
{
return value == null ? new Maybe<IEnumerable<T>>() : new Maybe<IEnumerable<T>>(value);
}