2013-03-03 7 views
6

Jako projekt hobbistyczny (i głębiej zanurzam się w metodach generycznych/rozszerzających) piszę bibliotekę do sprawdzania parametrów!W jaki sposób można dołączyć metodę rozszerzenia do klasy ogólnej, gdy argumentem typu jest IEnumerable <T>?

mam model o nazwie argument, że opisuje parametr i wygląda tak:

public class Argument<T> 
{ 
    internal Argument(string name, T value) 
    { 
     Name = name; 
     Value = value; 
    } 

    public string Name { get; private set; } 

    public T Value { get; private set; } 
} 

Po walidacji parametru rozpoczyna wystąpienie tego obiektu jest tworzony, a poszczególne walidacji są wykonywane przez wywołanie metody rozszerzenie (które zawierają rzeczywistą logikę), która się z tym wiąże.

Jedną z takich metod rozszerzenie sprawdza, czy kolekcja zawiera co najmniej jeden element, a obecnie wygląda następująco:

public static Argument<IEnumerable<T>> HasItems<T>(this Argument<IEnumerable<T>> argument) 
{ 
    if (!argument.Value.Any()) 
     throw Error.Generic(argument.Name, "Collection contains no items."); 

    return argument; 
} 

Ale to nie wydaje się działać. Gdybym był, powiedzmy, aby napisać ten test Jednostka:

[TestMethod] 
public void TestMethod1() 
{ 
    var argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 }; 

    Validate.Argument("argument", argument) 
     .IsNotNull() 
     .HasItems() 
     .All(v => v.IsGreaterThan(0)); 
} 

HasItems nie pojawi się w IntelliSense, i otrzymuję ten błąd kompilacji:

'Validation.Argument<System.Collections.Generic.List<int>>' does not contain a definition for 'HasItems' and no extension method 'HasItems' accepting a first argument of type 'Validation.Argument<System.Collections.Generic.List<int>>' could be found (are you missing a using directive or an assembly reference?)

A jeśli staram przepuszczenie wartość bezpośrednio do metody rozszerzenia, tak:

CollectionTypeExtensions.HasItems(Validate.Argument("argument", argument)); 

uzyskać to:

The best overloaded method match for 'Validation.CollectionTypeExtensions.HasItems<int>(Validation.Argument<System.Collections.Generic.IEnumerable<int>>)' has some invalid arguments

podstawie moich badań, co bym potrzebne do tego, aby praca nazywana jest „odchylenie” i odnosi się do interfejsów i delegatów, ale nie do klas (tj. Wszystkie klasy są niezmienne)

Że powiedział, może działać inaczej. Jeden, który przychodzi do głowy to przepisać go prosto do T, tak jak poniżej:

public static Argument<T> HasItems<T, TElement>(this Argument<T> argument) 
     where T : IEnumerable<TElement> 
{ 
    if (!argument.Value.Any()) 
     throw Error.Generic(argument.Name, "Collection contains no items."); 

    return argument; 
} 

..ale że nie działa albo ponieważ wymaga TElement być określony jawnie, gdy metoda jest wywoływana. Mógłbym też cofnąć się do używania nietypowego interfejsu IEnumerable w ograniczeniu typu, ale wtedy musiałbym albo znaleźć sposób na wymuszenie IEnumerable na IEnumerable (co wymagałoby poznania, co T jest w tym kontekście), albo powielenie funkcjonalność Any() w celu przetestowania istnienia jakichkolwiek elementów, i jest inna metoda rozszerzenia (All), która byłaby bardzo, bardzo niechlujna, więc wolałbym tego uniknąć.

Ostatecznie, myślę, że moje pytanie brzmi: w jaki sposób uzyskać właściwą metodę dołączania?

Odpowiedz

2

Czy to działa dla Ciebie? Wydaje się trochę kałuża, ale tak naprawdę działa.

public static Argument<T> HasItems<T>(this Argument<T> argument) where T: IEnumerable 
{ 
    if (!argument.Value.Cast<object>().Any()) 
    { 
     throw Error.Generic(argument.Name, "Collection contains no items."); 
    } 

    return argument; 
} 
+0

... To rzeczywiście działa! Kiedy spojrzałem na to świeżymi oczami dziś rano, wylądowałem na innej drodze, ale zaznaczam jako odpowiedź, ponieważ zwięźle rozwiązuje problem w przedstawiony sposób. Dzięki! –

0

wierzę, co rzeczywiście chcesz to interfejs IArgument który jest kowariantna na T:

public static class Validate 
{ 
    public static IArgument<T> Argument<T>(string name, T value) 
    { 
     return new Argument<T>(name, value); 
    } 
} 

public interface IArgument<out T> 
{ 
    string Name { get; } 
    T Value { get; } 
} 

public class Argument<T> : IArgument<T> 
{ 
    internal Argument(string name, T value) 
    { 
     Name = name; 
     Value = value; 
    } 

    public string Name { get; private set; } 

    public T Value { get; private set; } 
} 

public static class ExtensionMethods 
{ 
    public static IArgument<T> IsNotNull<T>(this IArgument<T> argument) 
    { 
     return argument; 
    } 

    public static IArgument<IEnumerable<T>> HasItems<T>(this IArgument<IEnumerable<T>> argument) 
    { 
     return argument; 
    } 

    public static IArgument<IEnumerable<T>> All<T>(this IArgument<IEnumerable<T>> argument, Predicate<T> predicate) 
    { 
     return argument; 
    } 
} 

[TestMethod] 
public void TestMethod1() 
{ 
    List<int> argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 }; 

    Validate.Argument("argument", argument) 
     .IsNotNull() 
     .HasItems() 
     .All(v => v > 0); 
} 
Powiązane problemy