#c# #generics #delegates
#c# #универсальные #делегаты
Вопрос:
Я пытаюсь создать делегат для задания значения универсального свойства, но я получаю сообщение об ошибке: Error binding to target method
при попытке выполнить следующий код:
Action<T, object> setValue = (Action<T, object>) Delegate.CreateDelegate(
typeof(Action<T, object>), null, property.GetSetMethod());
Возможно ли это вообще?
Комментарии:
1. Является ли ваше свойство статическим свойством? Если нет, то вы не можете передать значение null.
2. Нет, это не так, извините, я пропустил эту часть, но даже когда я передаю экземпляр класса, он работает некорректно, я все равно получаю то же исключение
Ответ №1:
Да, это возможно, вы просто пытаетесь создать делегат неправильного типа. Метод set свойства принимает только один аргумент, значение, которое вы собираетесь установить. Также, поскольку это метод экземпляра, вы должны передать целевой объект, к которому вы хотите его привязать, в вызове CreateDelegate.
Пример:
var setValue = (Action<T>)Delegate.CreateDelegate( typeof( Action<T> ), target, property.GetSetMethod() );
Ответ №2:
Это зависит. В моем ответе я предполагаю две вещи:
- Ваш тип «T» — это тип вашего класса (который я теперь буду обозначать как TClass)
- Тип «object» — это тип вашего свойства (которое я теперь буду обозначать как TProperty)
Поскольку ваше свойство нестатично, есть две возможности:
- «Обычный» делегат с прикрепленным целевым объектом (экземпляром).
- «Открытый» делегат, в котором целевой объект требуется в качестве первого входного параметра делегата.
Функция для создания такого «обычного» делегата создается следующим образом:
static public Action<TClass, TProperty> CreateSetPropertyDelegate<TClass, TProperty>(this PropertyInfo propertyInfo)
{
return (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), propertyInfo.GetSetMethod());
}
И используется (при условии, что тип свойства имеет тип int):
Action<int> setter = typeof(MyClass).GetProperty("MyProperty").CreateSetPropertyDelegate<MyClass, int>(myInsance);
setter(myPropertyValue);
Функция для создания открытого делегата:
static public Action<TClass, TProperty> CreateSetPropertyDelegate<TClass, TProperty>(this PropertyInfo propertyInfo)
{
return (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), propertyInfo.GetSetMethod());
}
И используется:
Action<MyClass, int> setter = typeof(MyClass).GetProperty("MyProperty").CreateSetPropertyDelegate<MyClass, int>();
setter(myInsance, myPropertyValue);
Ответ №3:
Я думаю, вы хотите этого:
Action<T, object> setValue = (t, o) => property.GetSetMethod().Invoke(t, new object[] { o });
или
Action<T, object> setValue = (t, o) => property.SetValue(t, o, null);
Редактировать
Чтобы проиллюстрировать предполагаемую более низкую производительность этого ответа по сравнению с принятым ответом, предположим, что этот метод:
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());
}
}
Ответ МерикоВОЙ использует отражение в GetSetter
методе, поэтому мы предполагаем, что GetSetter
метод в его подходе требует больше времени для выполнения. Этот ответ использует отражение при каждом нашем вызове setValue.Invoke
, поэтому мы предполагаем, что для выполнения в этом ответе требуется больше времени. Если мы предположим, что количество элементов в последовательности велико, ответ МерикоВОЙ должен потребовать меньше времени для выполнения.
Например, предположим, что выполнение метода GetSetter МерикоВОЙ занимает на X миллисекунд больше, чем у меня, в то время как мой делегат setValue занимает на Y миллисекунд больше, чем у него. Если в последовательности N элементов, то мое решение должно быть медленнее его на (N * Y — X) миллисекунд.
Комментарии:
1. Это использует PropertyInfo для установки значения каждый раз, это намного медленнее, чем то, что возвращает CreateDelegate
2. Можете ли вы сказать мне, почему этот метод будет медленнее? В моих тестах lambda выполнялся в два раза быстрее, чем create delegate.
3. @nawfal потому что с помощью этого метода
setValue
является ссылкой на делегат, который использует отражение для получения другого делегата, с помощью которого он устанавливает значение свойства. Каждый раз, когда вы вызываете референтsetValue
, вы используете reflection API. Метод в принятом ответе использует отражение для создания делегата, который устанавливает свойство напрямую, поэтому вы используете reflection API один раз, при назначенииsetValue
; вызовыsetValue
не используют отражение.4. @phoog но почему я получаю улучшенную производительность с вашим ответом, а не с ответом МерикоВОЙ? Или я должен сделать это отдельным вопросом?
5. @nawfal Я добавил обсуждение производительности к своему ответу, частично для того, чтобы прояснить разницу между «назначением переменной setValue» и «вызовом ее референта».