Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegate for Generic Property.GetSetMethod

I am trying to create a delegate for setting a property value of a generic, but I am getting an error: Error binding to target method when I try to execute the following code:

Action<T, object> setValue = (Action<T, object>) Delegate.CreateDelegate(
    typeof(Action<T, object>), null, property.GetSetMethod());

Is this even possible?

like image 328
ChandlerPelhams Avatar asked Nov 10 '11 23:11

ChandlerPelhams


3 Answers

Yes it is possible, you're just trying to create a delegate of the wrong type. The set method of a property only takes one argument, the value you're going to set. Also since its an instance method, you must pass the target object you want it to be bound to in the CreateDelegate call.

Example:

  var setValue = (Action<T>)Delegate.CreateDelegate( typeof( Action<T> ), target, property.GetSetMethod() );
like image 60
MerickOWA Avatar answered Oct 21 '22 21:10

MerickOWA


I think you want this:

Action<T, object> setValue = (t, o) => property.GetSetMethod().Invoke(t, new object[] { o });

or

Action<T, object> setValue = (t, o) => property.SetValue(t, o, null);

EDIT

To illustrate the assumed poorer performance of this answer compared to the accepted answer, assume this method:

void SetAnObjectPropertyOnALotOfObjects<T>(IEnumerable<T> objs)
{
    //Create a delegate and assign a reference to that delegate to the setValue variable
    Action<T, object> setValue = GetSetter();

    foreach (var o in objs)
    {
        //invoke the delegate referred to by the setValue variable (that is, "invoke its referent"
        setValue.Invoke(o, new object());
    }
}

MerickOWA's answer uses reflection in the GetSetter method, so we assume that the GetSetter method takes more time to execute, in his approach. This answer uses reflection each time we call setValue.Invoke, so we assume that takes more time to execute in this answer. If we assume the number of items in the sequence is large, MerickOWA's answer should require less time to execute.

For example, let's say that MerickOWA's GetSetter method takes X milliseconds more than mine to execute, while my setValue delegate takes Y milliseconds more than his. If there are N items in the sequence, then my solution should be slower than his by (N * Y - X) milliseconds.

like image 1
phoog Avatar answered Oct 21 '22 22:10

phoog


It depends. In my answer I assume two things:

  1. Your type "T" is the type of your class (which I will now denote as TClass)
  2. The type "object" is the type of your property (which I will now denote as TProperty)

Because your property is a non-static, there are two possibilies:

  1. An "normal" delegate with the target (instance) attached.
  2. An "open" delegate with in which a target is required as the first input parameter of the delegate.

A function to create such "normal" delegate is created as follows:

static public Action<TClass, TProperty> CreateSetPropertyDelegate<TClass, TProperty>(this PropertyInfo propertyInfo)
{
    return (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), propertyInfo.GetSetMethod());
}

And in use (assuming the property type is of type int):

Action<int> setter = typeof(MyClass).GetProperty("MyProperty").CreateSetPropertyDelegate<MyClass, int>(myInsance);
setter(myPropertyValue);

A function to create an open delegate:

static public Action<TClass, TProperty> CreateSetPropertyDelegate<TClass, TProperty>(this PropertyInfo propertyInfo)
{
    return (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), propertyInfo.GetSetMethod());
}

And in use:

Action<MyClass, int> setter = typeof(MyClass).GetProperty("MyProperty").CreateSetPropertyDelegate<MyClass, int>();
setter(myInsance, myPropertyValue);
like image 1
Martin Mulder Avatar answered Oct 21 '22 23:10

Martin Mulder