简体   繁体   中英

Dynamic dispatch by Type only known at runtime

I keep running across the need for the same pattern in my code, which frequently needs to validate the values of all the properties in an object. The pseudo code looks like this:

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.

I want the code to dynamically dispatch the value to the correct method based on the object's Type (which can be found by calling property.PropertType or value.GetType() assuming value is not null).

The only way I have found to make this work is something like this:

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

and then the Map enables dispatching the object to correct method by type:

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

Is there native C# (ie language feature) for doing this dynamic dispatch by Type at runtime?

Your approach is good if you want to keep a "nominal reflection execution". My recommandation is to avoid reflection as "normal" execution code.

It cause the need to work with extremely abstract objects (often System.Object). You can always use generic and/or reflection to produce on "fire/init time" a delegate adapted to your need without any Type resolution in "nonimal execution" and use the delegate naturally.

a short example to illustrate it

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

Implementation of the delegate

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

The dynamic keyword will do the downcast correctly. So the above code change be changed to:

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

The .NET Runtime then searches for the most specific method signature to invoke. I had previously thought that dynamic only worked for Duck Typing , but it also works for dynamic dispatch to overloaded methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM