5

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, 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

Community
  • 1
  • 1
anchandra
  • 1,089
  • 12
  • 23

3 Answers3

3

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();
   }
Community
  • 1
  • 1
Steven Jeuris
  • 18,274
  • 9
  • 70
  • 161
  • So how will i invoke this delegate to get the property value of my object obj? – anchandra Mar 07 '11 at 16:51
  • Thanks for the Code Review suggestion. I will give it a try – anchandra Mar 07 '11 at 19:00
  • @anchandra: I added some more information. If this example isn't clear, let me know, I might take a look at writing the implementation as well as I might need it also at some point. :) – Steven Jeuris Mar 07 '11 at 19:07
  • @anchandra: Never mind the example, jim31415's answer already has a specific implementation for the getter which will result in the same thing. Your implementation is indeed slightly more performant, but a lot less flexible. I'd argue you should only worry about performance when you notice it is required. – Steven Jeuris Mar 07 '11 at 19:18
  • Thanks for the update. Performance is however my main concern since i am expecting a large volume of data to process every second. – anchandra Mar 07 '11 at 19:32
2

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.

Community
  • 1
  • 1
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
2

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 );
jim31415
  • 8,588
  • 6
  • 43
  • 64
  • I tried the method described here, but performance-wise, it is less efficient than they way i am doing it now – anchandra Mar 07 '11 at 18:51