2013-03-03 6 views
6

趣味のプロジェクト(とジェネリックス/エクステンションメソッドに深く浸る)として、私はパラメータチェックライブラリを書いています!type引数が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)); 

私はこれを取得210私は仕事には、このために必要があると思います何私の研究に基づいて

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

ではなくクラスに、「分散」と呼ばれ、インターフェースや代表者に適用される(すなわち:。すべてのクラスは不変である)

ことそれは、別の方法で動作するかもしれないと言った。頭に浮かぶ一つはそうのように、Tに直行し、それを書き換えることである:メソッドが呼び出されたときに、それが明示的に指定するTElementを必要とするので、どちらか動作しません

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

..But。私はまたタイプの制約で非ジェネリックなIEnumerableインターフェースを使うことに落ちるかもしれませんが、IEnumerableをIEnumerableに強制する方法を見つけなければならないでしょう(これはTがそのコンテキストにあることを知る必要があります) という項目の存在をテストするためにAny()の機能性がありますが、非常に面倒な別の拡張メソッド(All)があります。

最終的に私の質問は、エクステンションメソッドを正しく添付するにはどうすればいいですか?

答えて

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

...確かに動作しないこと!私は今朝新鮮な目でこれを見ると、私は別のルートに行きましたが、提示された問題を簡潔に解決するため、私は答えとしてマークしています。ありがとう! –

0

T上の共変であるインターフェイスIArgument次のとおりです。

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); 
} 
関連する問題