开发者

Best practice instantiating generic delegates and accessing property getters

I want to create delegates to access properties of different objects without knowing them in advance.

I have the following definitions

public delegate T MyMethod<K, T>(K data);

public static MyMethod<K, 开发者_JS百科T> CreatePropertyGetter<K, T>(PropertyInfo property)
{       
   MethodInfo mi = property.DeclaringType.GetMethod("get_" + property.Name);        
return (MyMethod<K, T>)Delegate.CreateDelegate(typeof(MyMethod<K, T>), mi);
}

where T can be decimal, string, datetime or int

I have some initializing code that will create MyMethod delegates, based on the reflected properties of my object as follows:

foreach (PropertyInfo property in entityType.GetProperties())
{               
    switch (property.PropertyType.Name)
    {
        case "System.Decimal":
            return CreatePropertyGetter<T, decimal>(property);
        case "System.DateTime":
            return CreatePropertyGetter<T, DateTime>(property);
        case "System.String":
            return CreatePropertyGetter<T, DateTime>(property);
    }
}

Is there a better way to

  1. create property getters?
  2. enumerate through the supported property types hard-coded as strings?

EDIT:

My concern is performance, since these delegates will be called frequently (ticking scenario), so any casting will slow it down. While a more elegant solution is desirable, performance is still my main concern

I posted the same question on Code Review here, so i will mark this as solved considering the response there


This is something which could be posted on Code Review, and actually, I already posted a similar question. I believe my approach which uses expression trees already improves on your approach.

Example in usage:

Action<object> compatibleExecute =
    DelegateHelper.CreateCompatibleDelegate<Action<object>>( owner, method );

Conversions are done when necessary. The method passed to the function here can have any type of parameter.

UPDATE:

I haven't tested this, but in your case you could try the following:

Func<object> getter =
    DelegateHelper.CreateCompatibleDelegate<Func<object>>( owner, method );

method must be set to the getter you retrieved. owner must be set to the instance of your object. If you want to allow to pass the owner as an argument to the delegate, you'll have to adjust the code. An example is given by Vladimir Matveev as a comment on the article of Jon Skeet.

static Func<T, object, object> MagicMethod<T>(MethodInfo method)    
{    
    var parameter = method.GetParameters().Single();    
    var instance = Expression.Parameter(typeof (T), "instance");
    var argument = Expression.Parameter(typeof (object), "argument");

    var methodCall = Expression.Call(
        instance,
        method,
        Expression.Convert(argument, parameter.ParameterType)
        );

    return Expression.Lambda<Func<T, object, object>>(
        Expression.Convert(methodCall, typeof (object)),
        instance, argument
        ).Compile();
   }


Check out this article by Jon Skeet:

http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx

This method dynamically determines the return type of the getter.

public static class DelegateCreator
{
    //-----------------------------------------------------------------------
    public static Func<T, object> GetMethod<T>( PropertyInfo propertyInfo ) 
        where T : class
    {
        MethodInfo method = propertyInfo.GetGetMethod( true );
        if( method == null )
        {
            string msg = String.Format( "Property '{0}' does not have a getter.", propertyInfo.Name );
            throw new Exception( msg );
        }

        // First fetch the generic form
        MethodInfo genericHelper = typeof( DelegateCreator ).GetMethod( "CreateGetHelper",
             BindingFlags.Static | BindingFlags.NonPublic );


        // Now supply the type arguments
        MethodInfo constructedHelper = genericHelper.MakeGenericMethod
             ( typeof( T ), method.ReturnType );

        // Now call it. The null argument is because it's a static method.
        object ret = constructedHelper.Invoke( null, new object[] { method } );

        // Cast the result to the right kind of delegate and return it
        return (Func<T, object>)ret;
    }

    //-----------------------------------------------------------------------
    static Func<TTarget, object> CreateGetHelper<TTarget, TReturn>( MethodInfo method )
        where TTarget : class
    {
        // Convert the slow MethodInfo into a fast, strongly typed, open delegate
        Func<TTarget, TReturn> func = (Func<TTarget, TReturn>)Delegate.CreateDelegate
             ( typeof( Func<TTarget, TReturn> ), method );

        // Now create a more weakly typed delegate which will call the strongly typed one
        Func<TTarget, object> ret = ( TTarget target ) => func( target );
        return ret;
    }

}

Used like this:

PropertyInfo pi = typeof( Employee ).GetProperty( "LastName" );

Action<Employee, object> getMethod = DelegateCreator.SetMethod<Employee>( pi );
string lastName = getMethod( employee );


A Better Way:

  1. Use Expression<TDelegate>. For example:

    public static class PropertyExpressionHelper {

    public static TProperty GetProperty<T,TProperty>(this T obj, Expression<Func<T,TProperty>> getPropertyExpression)
    {
        if(obj == null)
        {
            throw new ArgumentNullException("obj");
        }
        if(getPropertyExpression==null)
        {
            throw new ArgumentNullException("getPropertyExpression");
        }
        var memberExpression = getPropertyExpression.Body as MemberExpression;
        bool memberExpressionIsInvalidProperty = memberExpression == null ||
                                                 !(memberExpression.Member is PropertyInfo &&
                                                   memberExpression.Expression.Type == typeof (T));
        if(memberExpressionIsInvalidProperty)
        {
            throw new ArgumentNullException("getPropertyExpression", "Not a valid property expression.");
        }
        return (TProperty)(memberExpression.Member as PropertyInfo).GetValue(obj, null);
    }
    

    }

  2. To get an enumerable of the types of all the properties in a type, do this: typeof(T).GetProperties().Select(x=>x.PropertyType).Distinct();

Look at the source code someone wrote for a C# TypeSwitch, available in this post. I think it might have what you are looking for.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜