2016-03-29 1 views
2

J'implémente une bibliothèque d'assertions d'arguments fluides où l'accent est mis sur la vérification de type forte lors de la compilation. Intellisense devrait seulement montrer les méthodes et les extensions disponibles pour le type affirmé.Méthodes d'extension sur une interface générique où T est Enumerable <K>

Je rencontre des problèmes pour résoudre les arguments de type correct lors de la création d'une extension pour IEnumerable.

idée dans la bibliothèque est que vous pouvez appeler ThrowIf (ou ThrowIfNot) sur tout type qui vous renvoie une instance d'affirmation de type IAssertion:

public static IAssertion<T> ThrowIf<T>(this T t) 
{ 
    return new IfAssertion<T>(t); 
} 

Maintenant, je veux vérifier contre IEnumerable si elle contient un élément spécifique. Il y aura deux surcharges où l'on prend l'objet de type T en tant que paramètre et l'autre prend une fonction où faire l'évaluation:

public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, T2 item) 
    where T1 : IEnumerable<T2> 
{ 
    // assertion logic 
    return assertion.Value; 
} 

public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, Func<T2, bool> func) 
    where T1 : IEnumerable<T2> 
{ 
    // assertion logic 
    return assertion.Value; 
} 

Tout va bien lors de l'utilisation de la surcharge en prenant une instance du type réel. Mais celui-ci un avec le compilateur de fonction ne peut pas déduire les arguments de type correctement à moins casting est composé:

var list = new List<string>(); 
list.ThrowIf().Contains("foo"); // compiles 
list.ThrowIf().Contains((string s) => false); // compiles 
list.ThrowIf().Contains(s => false); // does not compile 

Est-il possible que je pourrais faire le compilateur heureux sans faire le casting pour le paramètre de la fonction?

Plus de détails de mise en œuvre peuvent être trouvés d'ici: https://bitbucket.org/mikalkai/argument-assertions/overview

+0

Je pense que vous pouvez seulement faire 'contains (s => false) ' –

+0

Non. Vous devrez l'appeler Contient , string> (s => false). – mikalkai

Répondre

3

Avertissement: Cette réponse est valable uniquement si IAssertion peut être fait covariant.

En supposant que IAssertion est covariant, vous ne devez pas nécessairement deux paramètres de type générique T1 et T2 pour les méthodes Contains. Au lieu de cela, vous spécifiez IEnumerable dans votre interface directement et utiliser un seul paramètre de type générique comme ceci:

public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, T item) 
{ 
    // assertion logic 
    return assertion.Value; 
} 

public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, Func<T, bool> func) 
{ 
    // assertion logic 
    return assertion.Value; 
} 

Ensuite, vous pouvez utiliser la méthode Contains comme ceci:

var list = new List<string>(); 
list.ThrowIf().Contains("foo"); // compiles 
list.ThrowIf().Contains((string s) => false); // compiles 
list.ThrowIf().Contains(s => false); // compiles now too 
+0

IAssertion peut et est covariante. J'ai effectivement essayé de lier l'extension directement dans le IEnumerable mais pour une raison quelconque intellisense disait qu'il ne peut pas trouver la méthode. Eh bien, je l'ai juste essayé sur une machine différente et Tzadam, tout a bien fonctionné! Ce n'est pas la première fois qu'intellisense se retourne. – mikalkai