2013-03-03 50 views
6

作爲一個業餘愛好項目(並將自己更深入的泛型/擴展方法),我正在寫一個參數檢查庫!當類型參數爲IEnumerable <T>時,如何將擴展方法附加到泛型類?

我有一個模型叫參數描述參數,看起來像這樣:

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; } 
} 

在參數驗證開始,該對象的實例被創建,以及個人驗證通過調用擴展方法進行(包含實際的邏輯),掛起它。

一個這樣的擴展方法驗證集合包含至少一個項目,目前看起來是這樣的:

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; 
} 

但它似乎沒有工作。如果是我,說,寫這個單元測試:

[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不會在智能感知顯示,我得到這個編譯錯誤:

'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?)

如果我嘗試傳遞直接進入價值擴展方法,像這樣:

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

我得到這個:

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

根據我的研究,我會需要這個工作被稱爲「變異」,並適用於接口和委託,而不是類(即:所有的類都是不變)

那說,它可能以另一種方式工作。一個想到的是重寫它直奔T,像這樣:

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; 
} 

..但不工作,要麼因爲它需要TElement明確指定調用方法時。我也可以回到在類型約束中使用非泛型IEnumerable接口,但是我必須找到一種方法將IEnumerable強制爲IEnumerable(這需要知道T在這個上下文中),或者重複Any()爲了測試是否存在任何項目的功能,還有另一個擴展方法(全部),這將是非常非常混亂的,所以我寧願避免它。

所以最終,我想我的問題是:如何讓我的擴展方法正確連接?

回答

2

這是否適合您?看起來有些狡猾,但確實有效。

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

那......確實工作!當我今天早上用新鮮的眼睛看這個時,我最終走了一條不同的路線,但我標記爲答案,因爲它簡潔地解決了所提出的問題。謝謝! – 2013-03-03 19:43:18

0

我相信你真正想要的是一個接口IArgument這是對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); 
} 
相關問題