Как создать делегат для свойства .NET?
Я пытаюсь создать делегат (как тест) для:
Public Overridable ReadOnly Property PropertyName() As String
Моя интуитивная попытка объявила делегата следующим образом:
Public Delegate Function Test() As String
И создайте экземпляр следующим образом:
Dim t As Test = AddressOf e.PropertyName
Но это вызывает ошибку:
Метод 'Открытый переопределяемое свойство ReadOnly PropertyName() As Строка 'не имеет подписи совместим с делегатом "Делегат" Function Test() As String '.
Итак, потому что я имел дело с собственностью, я пробовал это:
Public Delegate Property Test() As String
Но это порождает ошибку компилятора.
Итак, вопрос в том, как мне сделать делегат для свойства?
Смотрите эту ссылку:
http://peisker.net/dotnet/propertydelegates.htm
Ответы
Ответ 1
Возьмем проблему, используя AddressOf - если вы знаете имя проперта во время компиляции, вы можете (на С#, по крайней мере) использовать anon-method/lambda:
Test t = delegate { return e.PropertyName; }; // C# 2.0
Test t = () => e.PropertyName; // C# 3.0
Я не эксперт VB, но рефлектор утверждает, что это то же самое, что:
Dim t As Test = Function
Return e.PropertyName
End Function
Это работает?
Оригинальный ответ:
Вы создаете делегаты для свойств с Delegate.CreateDelegate
; это может быть открыто для любого экземпляра типа, фиксированного для одного экземпляра, и может быть для getter или setter; Я приведу пример в С#...
using System;
using System.Reflection;
class Foo
{
public string Bar { get; set; }
}
class Program
{
static void Main()
{
PropertyInfo prop = typeof(Foo).GetProperty("Bar");
Foo foo = new Foo();
// create an open "getter" delegate
Func<Foo, string> getForAnyFoo = (Func<Foo, string>)
Delegate.CreateDelegate(typeof(Func<Foo, string>), null,
prop.GetGetMethod());
Func<string> getForFixedFoo = (Func<string>)
Delegate.CreateDelegate(typeof(Func<string>), foo,
prop.GetGetMethod());
Action<Foo,string> setForAnyFoo = (Action<Foo,string>)
Delegate.CreateDelegate(typeof(Action<Foo, string>), null,
prop.GetSetMethod());
Action<string> setForFixedFoo = (Action<string>)
Delegate.CreateDelegate(typeof(Action<string>), foo,
prop.GetSetMethod());
setForAnyFoo(foo, "abc");
Console.WriteLine(getForAnyFoo(foo));
setForFixedFoo("def");
Console.WriteLine(getForFixedFoo());
}
}
Ответ 2
Я просто создаю помощника с довольно хорошей производительностью:
http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html
Он не использует подход IL/Emit и очень быстро!
Редактировать по oscilatingcretin 2015/10/23
Источник содержит некоторые проблемы с корпусом и свойственные =""
, которые необходимо удалить. Прежде чем набрать гниль, мне показалось, что я бы опубликовал очищенную версию источника для удобной копии макаронных изделий, а также пример того, как ее использовать.
Пересмотренный источник
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Tools.Reflection
{
public interface IPropertyAccessor
{
PropertyInfo PropertyInfo { get; }
object GetValue(object source);
void SetValue(object source, object value);
}
public static class PropertyInfoHelper
{
private static ConcurrentDictionary<PropertyInfo, IPropertyAccessor> _cache =
new ConcurrentDictionary<PropertyInfo, IPropertyAccessor>();
public static IPropertyAccessor GetAccessor(PropertyInfo propertyInfo)
{
IPropertyAccessor result = null;
if (!_cache.TryGetValue(propertyInfo, out result))
{
result = CreateAccessor(propertyInfo);
_cache.TryAdd(propertyInfo, result); ;
}
return result;
}
public static IPropertyAccessor CreateAccessor(PropertyInfo PropertyInfo)
{
var GenType = typeof(PropertyWrapper<,>)
.MakeGenericType(PropertyInfo.DeclaringType, PropertyInfo.PropertyType);
return (IPropertyAccessor)Activator.CreateInstance(GenType, PropertyInfo);
}
}
internal class PropertyWrapper<TObject, TValue> : IPropertyAccessor where TObject : class
{
private Func<TObject, TValue> Getter;
private Action<TObject, TValue> Setter;
public PropertyWrapper(PropertyInfo PropertyInfo)
{
this.PropertyInfo = PropertyInfo;
MethodInfo GetterInfo = PropertyInfo.GetGetMethod(true);
MethodInfo SetterInfo = PropertyInfo.GetSetMethod(true);
Getter = (Func<TObject, TValue>)Delegate.CreateDelegate
(typeof(Func<TObject, TValue>), GetterInfo);
Setter = (Action<TObject, TValue>)Delegate.CreateDelegate
(typeof(Action<TObject, TValue>), SetterInfo);
}
object IPropertyAccessor.GetValue(object source)
{
return Getter(source as TObject);
}
void IPropertyAccessor.SetValue(object source, object value)
{
Setter(source as TObject, (TValue)value);
}
public PropertyInfo PropertyInfo { get; private set; }
}
}
Использовать его следующим образом:
public class MyClass
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
MyClass e = new MyClass();
IPropertyAccessor[] Accessors = e.GetType().GetProperties()
.Select(pi => PropertyInfoHelper.CreateAccessor(pi)).ToArray();
foreach (var Accessor in Accessors)
{
Type pt = Accessor.PropertyInfo.PropertyType;
if (pt == typeof(string))
Accessor.SetValue(e, Guid.NewGuid().ToString("n").Substring(0, 9));
else if (pt == typeof(int))
Accessor.SetValue(e, new Random().Next(0, int.MaxValue));
Console.WriteLine(string.Format("{0}:{1}",
Accessor.PropertyInfo.Name, Accessor.GetValue(e)));
}
Ответ 3
Вот версия С#/.NET 2.0 ответ Марка Гравелла:
using System;
using System.Reflection;
class Program
{
private delegate void SetValue<T>(T value);
private delegate T GetValue<T>();
private class Foo
{
private string _bar;
public string Bar
{
get { return _bar; }
set { _bar = value; }
}
}
static void Main()
{
Foo foo = new Foo();
Type type = typeof (Foo);
PropertyInfo property = type.GetProperty("Bar");
// setter
MethodInfo methodInfo = property.GetSetMethod();
SetValue<string> setValue =
(SetValue<string>) Delegate.CreateDelegate(typeof (SetValue<string>), foo, methodInfo);
setValue("abc");
// getter
methodInfo = property.GetGetMethod();
GetValue<string> getValue =
(GetValue<string>) Delegate.CreateDelegate(typeof (GetValue<string>), foo, methodInfo);
string myValue = getValue();
// output results
Console.WriteLine(myValue);
}
}
Опять же, " Delegate.CreateDelegate" является тем, что является основополагающим для этого примера.
Ответ 4
Это хорошая идея
Test t = () => e.PropertyName; // C# 3.0
Но будьте осторожны, если вы делаете что-то вроде этого:
List<Func<int>> funcs = new List<Func<int>>();
foreach (var e in Collection)
funcs.Add(new Func<int>(() => e.Property));
Вызов:
foreach(var f in funcs)
f();
Всегда возвращает значение свойства объекта last в коллекции
В этом случае вы должны вызвать метод:
foreach (var e in Collection)
funcs.Add(new Func<int>(e.GetPropValue));
Ответ 5
Здесь пример С#, но все типы одинаковы:
Сначала создайте интерфейс (делегат). Помните, что метод, который вы присоединяете к своему делегату, должен возвращать тот же тип и принимать те же параметры, что и объявление делегата.
Не определяйте своего делегата в той же области, что и ваше событие.
public delegate void delgJournalBaseModified();
Сделайте событие на основе делегата:
public static class JournalBase {
public static event delgJournalBaseModified evntJournalModified;
};
Определите метод, который может быть привязан к вашему событию, которое имеет интерфейс, идентичный делегату.
void UpdateEntryList()
{
}
Свяжите метод с событием. Метод вызывается при запуске события. Вы можете связать столько методов с вашим мероприятием. Я не знаю предела. Это, наверное, что-то сумасшедшее.
JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList);
Что здесь происходит, этот метод добавляется как обратный вызов для вашего события. Когда событие будет запущено, ваш метод будет вызван.
Далее мы создаем метод, который будет запускать событие при вызове:
public static class JournalBase {
public static void JournalBase_Modified()
{
if (evntJournalModified != null)
evntJournalModified();
}
};
Затем вы просто вызываете метод - JournalBase_Modified() - где-то в вашем коде, и все методы, связанные с вашим событием, вызываются тоже один за другим.
Ответ 6
Версия VB:
Dim prop As PropertyInfo = GetType(foo).GetProperty("bar")
Dim foo1 As New foo
Dim getForAnyFoo As Func(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of foo, String)), Nothing, prop.GetGetMethod()), Func(Of foo, String))
Dim setForAnyFoo As Action(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of foo, String)), Nothing, prop.GetSetMethod()), Action(Of foo, String))
Dim getForFixedFoo As Func(Of String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of String)), foo1, prop.GetGetMethod()), Func(Of String))
Dim setForFixedFoo As Action(Of String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of String)), foo1, prop.GetSetMethod()), Action(Of String))
setForAnyFoo(foo1, "abc")
Debug.WriteLine(getForAnyFoo(foo1))
setForFixedFoo("def")
Debug.WriteLine(getForFixedFoo())