2016-01-15 1 views
10

関数に渡される不正なパラメータなどから保護するための簡単なGuard APIを構築しています。Fluent Nested Guard APIを構築するには

void DummyMethod(int? someObject) { 

    Guard.Ensure(someObject, "someObject") 
     .IsNotNull() 
     .IsGreaterThan(0) 
     .IsLessThan(10); 
} 

このすべての作品:コードは(これは単なるダム一例であることに注意)と同様に使用することができます現時点では

public static class Guard 
{ 
    public static GuardArgument<T> Ensure<T>(T value, string argumentName) 
    { 
     return new GuardArgument<T>(value, argumentName); 
    } 
} 

public class GuardArgument<T> 
{ 
    public GuardArgument(T value, string argumentName) 
    { 
     Value = value; 
     Name = Name; 
    } 

    public T Value { get; private set; } 
    public string Name { get; private set; } 
} 

// Example extension for validity checks 
public static GuardArgument<T> IsNotNull<T>(this GuardArgument<T> guardArgument, string errorMessage) 
{ 
    if (guardArgument.Value == null) 
    { 
     throw new ArgumentNullException(guardArgument.Name, errorMessage); 
    } 

    return guardArgument; 
}  

私は、次のコードを持っています良い。私は今、行うことができるようにしたいことは、次のようにチェックで子プロパティを含めるようにAPIを拡張している。

Guard.Ensure(someObject, "someObject") 
    .IsNotNull() 
    .Property(
     (x => x.ChildProp1, "childProp1") 
      .IsNotNull() 
      .IsGreaterThan(10) 
    ) 
    .Property(
     (x => x.ChildProp2, "childProp2") 
      .IsNotNull() 
      .IsLessThan(10) 
    ); 

明らかに新しい.Property方法が連鎖するために、親GuardArgumentを返す必要があります。さらに、子プロパティは、コードの重複を避けるために既存のチェックメソッド(IsNotNull()など)を使用できる必要があります。

ラムダ/プロパティ関数のパラメータを構築する方法や、.Propertyメソッドを配置する場所、つまりGuardArgumentなどのプロパティである必要があります。つまり、APIの構造がさらに改善されていても。

+1

はタイプミスかもしれませんが、GuardArgumentコンストラクタはあなたが明らかに新しい '.Property'方法を順に親' GuardArgument'を返す必要がある」、と言う '名= NAME' –

+1

設定しますチェーンに。 - いいえ、それは間違っています。あなたが流暢なAPIを作ろうとしているなら、親を渡すべきではありません。親を入れ子にする新しい 'GuardArgument'を構築しなければなりません。そうでなければ、鎖に沿って参照を保持し、それらから。 – Enigmativity

答えて

7

次の関数を使用すると、似たような構文を使用できます。

public static GuardArgument<T> Property<T, TProp>(this GuardArgument<T> guardArgument, Func<T, TProp> getProperty, string propertyName, Action<GuardArgument<TProp>> validate) 
{ 
    GuardArgument<TProp> propertyGuardArgument = new GuardArgument<TProp>(getProperty(guardArgument.Value), propertyName); 

    validate(propertyGuardArgument); 

    return guardArgument; 
} 

機能は、選択したプロパティの新しいGuardArgumentを作成し、あなたが望むようあなたが検証できるようにするためにActionパラメータにこれを渡します。

これにより、プロパティを無限に連鎖させることもできますが、特に読みやすいとは思えません。

使用法:

Guard.Ensure(someObject, "someObject") 
    .IsNotNull() 
    .Property(x => x.ChildProp1, "childProp1", childProp1 => 
     childProp1.IsNotNull() 
        .IsLessThan(10) 
        .Property(y => y.InnerChildProperty, "innerChildProperty", innerChildProperty => 
         innerChildProperty.IsNotNull() 
        ) 
    ) 
    .Property(x => x.ChildProp2, "childProp2", childProp2 => 
     childProp2.IsNotNull() 
        .IsGreaterThan(10) 
    ); 
+0

JavaScriptのように見えます; – jlvaquero

+0

とにかく、FuncとActionを流暢の外で宣言して作成してからProperty()に渡すことができます。 – jlvaquero

0

親オブジェクトチェックのチェーンにプロパティチェックを入れても効果がないと思います。ですから、私は親オブジェクトのために1つのチェーンを作り、それぞれのプロパティに対して別の鎖を作ることを勧めます。これは、はるかに読みやすいです:

Guard.Ensure(a, "a") 
    .IsNotNull("a is null"); 
    Guard.Ensure(a.p0, "a.p0") 
    .IsGreaterThan(10); 
    Guard.Ensure(a.p1, "a.p1") 
    .IsGreaterThan(5); 
0

私はあなたがここに車輪の再発明していると思います。この拡張機能をインストールしてください - Code Contractsここにはdocsの使い方があります。ベースのコードに加えて

すなわち、あなたに似アサート:

public int[] Bar(){ 
    Contract.Ensures(Contract.ForAll(0, Contract.Result<int[]>().Length, index => Contract.Result<int[]>()[index] > 0)); 

.... 
} 

又は

Contract.Requires<ArgumentNullException>(x.Value.NestedObject != null, ”x.Value.NestedObject”); 

しかし、また、属性とインターフェイスをチェックするための機能の豊富なセット、素敵な前および後を有しています条件などをチェック!

+2

私はプロジェクトでコード契約を使用して、使用するのに苦痛を感じました。彼らはクロールにビルドのスピードを削減します。数ヶ月後、私たちはそれらを取り出して、代わりに質問のようなものに行きました。 – Sean

+2

@Seanさん、ありがとう、私たちは最近、それらを使用して開始し、たくさん持っていないので、まだビルド時間がはるかに悪化する気づいていない。 – vittore

+0

私は既にCode Contractsを見てきました。Seanのように、ビルドスピードが遅く、ガーディングメソッドの入力に実際に使用されていました。したがって、軽量で軽量なものが必要です。 – Graham

関連する問題