2016-12-02 6 views
3

私はオブジェクト内のすべてのプロパティの値を検証する必要がある同じパターンの必要性を、コード内で実行し続けます。擬似コードは次のようになります。実行時にのみ知られているタイプによる動的ディスパッチ

bool ValidateMe(object c) { 
    var properties = GetProperties(c); 
    foreach (var property in properties) { 
     var value = property.GetValue(c); 
     if (!IsValid(value)) { 
      return false; 
     } 
    } 
    return true; 
} 

bool IsValid(int value) 
{ 
    return value != int.MaxValue; 
} 

bool IsValid(double value) 
{ 
    return value != double.MaxValue; 
} 

bool IsValid(object value) 
{ 
    return value != null; 
} // etc. 

私は、コードが動的に(値がnullでないと仮定しproperty.PropertTypeまたはvalue.GetType()を呼び出すことによって見つけることができます)、オブジェクトのタイプに基づいて正しい方法に値を派遣したいと思います。

私はこの仕事をするために発見した唯一の方法は、このようなものです:

interface IValidator { 
    bool IsValid(object value); 
} 

class PredicateValidator<T> : IValidator { 
    private Predicate<T> method; 
    PredicateValidator(Predicate<T> method) { 
     this.method = method; 
    } 

    bool IsValid(object value) { 
     return IsValid((T) value); 
    } 

    bool IsValid(T value) { 
     return method.invoke(value); 
    } 
} 


var validationsByType = new Dictionary<Type,IValidator>(); 
validationsByType[typeof(double)]=new PredicateValidator<double>(IsValid); 
validationsByType[typeof(double)]=new PredicateValidator<int>(IsValid); 

、その後、地図の種類によって方法を修正するために、オブジェクトを派遣できます

object value = property.GetValue(c); 
bool result = validationsByType[c.GetType()].IsValid(value); 

が原産であります実行時にこのタイプの動的ディスパッチを行うためのC#(つまり言語機能)?

+1

Dictionary >を作成して、バリデータクラスを追加することなく同じことを行うことはできますが、このアプローチはうまく見えます。 – Evk

+0

@evk:良い点、ありがとう。 –

+1

コードが実際に正常に動作しているので、私はこの質問を議論の対象外としています。多分、http://codereview.stackexchange.com/に投稿してください。 – nozzleman

答えて

0

それを説明する短い例。したがって、上記のコードの変更をに変更する:

bool ValidateMe(object c) { 
    var properties = GetProperties(c); 
    foreach (var property in properties) { 
     var value = property.GetValue(c); 
     if (!IsValid((dynamic) value)) { 
      return false; 
     } 
    } 
    return true; 
} 

bool IsValid(int value) 
{ 
    return value != int.MaxValue; 
} 

bool IsValid(double value) 
{ 
    return value != double.MaxValue; 
} 

.NETランタイムは、次に起動する最も特定メソッドシグネチャを検索します。以前はdynamicDuck Typingでしか動作していないと思っていましたが、オーバーロードされたメソッドへの動的ディスパッチでも機能します。

1

"公称反射実行"を維持したい場合、あなたのアプローチは良いことです。 私の推薦は、「通常の」実行コードとしてのリフレクションを避けることです。

非常に抽象的なオブジェクト(多くの場合、System.Object)で作業する必要があります。 ジェネリックおよび/またはリフレクションを使用して、いつでも「非実行」でタイプ解決なしに必要に応じた代理人を「ファイア/初期化時間」に作成し、代理人を自然に使用することができます。

dynamicキーワードが正しくdowncastを行いますデリゲートの

static public Validation 
{ 
    //Simply call the delegate (generic method is easier to use than generic class) 
    static public bool Validate<T>(T value) 
    { 
     return Validation<T>.Validate(value); 
    } 
} 

実装

static public Validation<T> 
{ 
    static public readony Fun<T, bool> Validate; 

    static Validation 
    { 
     if (typeof(T) == typeof(string)) 
     { 
      Validation<T>.Validate = new Func<T, bool>(value => 
      { 
       var _string = (string)(object)value; 
       //Do your test here 
       return true; 
      }); 
     } 
     else if (typeof(T) == typeof(int)) 
     { 
      Validation<T>.Validate = new Func<T, bool>(value => 
      { 
       var _int32 = (int)(object)value; 
       //Do your test here 
       return true; 
      }); 
     } 
     //... 
     else if (typeof(T).IsClass) 
     { 
      var validate = typeof(Validation).GetMethod("Validate"); 
      var parameter = Expression.Parameter(typeof(T)); 
      var properties = typeof(T).GetProperties(); 
      if (properties.length < 0) 
      { 
       Validation<T>.Validate = new Func<T, bool>(value => true); 
      } 
      else 
      { 
       var body = Expression.Constant(true); 
       foreach (var property in properties) 
       { 
        body = Expression.Condition(Expression.Call(validate.MakeGenericMethod(property.PropertyType), parameter), body, Expression.Constant(false)); 
       } 
       Validation<T>.Validate = Expression.Lambda<Func<T, bool>>(body, parameter).Compile(); 
      } 
     } 
    } 
} 
関連する問題