动态调度按类型仅在运行时已知

时间:2016-12-02 10:49:09

标签: c# reflection

我一直在我的代码中运行相同模式的需求,这经常需要验证对象中所有属性的值。伪代码如下所示:

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.

我希望代码根据对象的类型动态地将值分配给正确的方法(可以通过调用property.PropertTypevalue.GetType()来找到,假设值不为null)。

我发现做这项工作的唯一方法是这样的:

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

然后Map允许按类型调度对象以更正方法:

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

在运行时通过Type执行此动态调度是否有本机C#(即语言功能)?

2 个答案:

答案 0 :(得分:1)

如果您想保持“名义反思执行”,那么您的方法很好。 我的建议是避免反射为“正常”执行代码。

它需要使用极其抽象的对象(通常是System.Object)。 您总是可以使用泛型和/或反射在“fire / init time”上生成一个适合您需要的委托,而不需要“非执行”中的任何类型解析,并自然地使用委托。

一个简短的例子来说明它

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

答案 1 :(得分:1)

dynamic关键字将正确执行downcast。因此,上面的代码更改将更改为:

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 Runtime将搜索要调用的最具体的方法签名。我之前认为dynamic仅适用于Duck Typing,但它也可用于动态调度重载方法。