2013-03-03 28 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); 
} 
相关问题