如何为.NET属性创建委托?

时间:2022-11-16 07:22:47

I am trying to create a delegate (as a test) for:

我正在尝试创建一个委托(作为测试):

Public Overridable ReadOnly Property PropertyName() As String

My intuitive attempt was declaring the delegate like this:

我的直觉尝试是声明代表这样:

Public Delegate Function Test() As String

And instantiating like this:

并实例化如下:

Dim t As Test = AddressOf e.PropertyName

But this throws the error:

但这会引发错误:

Method 'Public Overridable ReadOnly Property PropertyName() As String' does not have a signature compatible with delegate 'Delegate Function Test() As String'.

方法'Public Overridable ReadOnly Property PropertyName()As String'没有与委托'Delegate Function Test()As String'兼容的签名。

So because I was dealing with a property I tried this:

因为我正在处理一个属性,我试过这个:

Public Delegate Property Test() As String

But this throws a compiler error.

但这会引发编译器错误。

So the question is, how do I make a delegate for a property?

所以问题是,我如何为一个财产代表?


See this link:

看到这个链接:

http://peisker.net/dotnet/propertydelegates.htm

6 个解决方案

#1


Re the problem using AddressOf - if you know the prop-name at compile time, you can (in C#, at least) use an anon-method / lambda:

使用AddressOf解决问题 - 如果你在编译时知道prop-name,你可以(至少在C#中)使用anon-method / lambda:

Test t = delegate { return e.PropertyName; }; // C# 2.0
Test t = () => e.PropertyName; // C# 3.0

I'm not a VB expert, but reflector claims this is the same as:

我不是VB专家,但反射器声称这与以下相同:

Dim t As Test = Function 
    Return e.PropertyName
End Function

Does that work?

那样有用吗?


Original answer:

You create delegates for properties with Delegate.CreateDelegate; this can be open for any instance of the type, of fixed for a single instance - and can be for getter or setter; I'll give an example in C#...

您可以使用Delegate.CreateDelegate为属性创建委托;这可以对任何类型的实例打开,对于单个实例是固定的 - 并且可以用于getter或setter;我将在C#中给出一个例子......

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


I just create an helper with pretty good performance : http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html It don't use IL / Emit approach and it is very fast !

我只是创建了一个具有相当好性能的帮助程序:http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html它不使用IL / Emit方法,而且速度非常快!

Edit by oscilatingcretin 2015/10/23

由oscilatingcretin编辑2015/10/23

The source contains some casing issues and peculiar ="" that have to be removed. Before link rot sets in, I thought I'd post a cleaned-up version of the source for easy copy pasta, as well as an example of how to use it.

源包含一些套管问题,并且必须删除特殊的“”。在链接腐烂之前,我想我会发布一个清理版本的源码,以便于复制意大利面,以及如何使用它的示例。

Revised source

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; }
    }
}

Use it like this:

像这样用它:

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


Here is a C#/.NET 2.0 version of Marc Gravell's response:

这是Marc Gravell的C#/。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);
 }
}

Again, 'Delegate.CreateDelegate' is what is fundamental to this example.

同样,'Delegate.CreateDelegate'是这个例子的基础。

#4


This is good idea

这是个好主意

Test t = () => e.PropertyName; // C# 3.0

But take care if your are doing something like this:

但要注意你是否正在做这样的事情:

List<Func<int>> funcs = new List<Func<int>>();
foreach (var e in Collection)
   funcs.Add(new Func<int>(() => e.Property));

Calling this:

foreach(var f in funcs)
   f();

Will always return value of property of the last object in Collection

将始终返回Collection中最后一个对象的属性值

In this case you should call method:

在这种情况下,您应该调用方法:

foreach (var e in Collection)
   funcs.Add(new Func<int>(e.GetPropValue));

#5


Here's a C# example but all the types are the same:

这是一个C#示例,但所有类型都是相同的:

First create the interface(delegate). Remember, a method that you attach to your delegate must return the same type, and take the same parameters as your delegate's declaration. Don't define your delegate in the same scope as your event.

首先创建接口(委托)。请记住,附加到委托的方法必须返回相同的类型,并采用与委托声明相同的参数。不要在与事件相同的范围内定义委托。

public delegate void delgJournalBaseModified();        

Make an event based on the delegate:

根据代表制作活动:

public static class JournalBase {
    public static event delgJournalBaseModified evntJournalModified;
};

Define a method that can be tied to your event that has an interface identical to the delegate.

定义一个方法,该方法可以绑定到具有与委托相同的接口的事件。

void UpdateEntryList()
{
}

Tie the method to the event. The method is called when the event is fired. You can tie as many methods to your event. I don't know the limit. It's probably something crazy.

将方法绑定到事件。触发事件时调用该方法。您可以将多种方法绑定到事件中。我不知道极限。这可能是疯狂的事情。

 JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList);

What happens here is the method is added as a callback for your event. When the event is fired, your method(s) will be called.

这里发生的是该方法被添加为您的事件的回调。触发事件时,将调用您的方法。

Next we make a method that will fire the event when called:

接下来,我们创建一个方法,在调用时触发事件:

public static class JournalBase {
    public static  void JournalBase_Modified()
    {
    if (evntJournalModified != null)
        evntJournalModified();
    }
};

Then you simply call the method -- JournalBase_Modified() -- somewhere in your code and all methods tied to your event are called too, one after another.

然后你只需调用方法 - JournalBase_Modified() - 在你的代码中的某个地方,所有与你的事件相关的方法也会被一个接一个地调用。

#6


VB version:

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())

#1


Re the problem using AddressOf - if you know the prop-name at compile time, you can (in C#, at least) use an anon-method / lambda:

使用AddressOf解决问题 - 如果你在编译时知道prop-name,你可以(至少在C#中)使用anon-method / lambda:

Test t = delegate { return e.PropertyName; }; // C# 2.0
Test t = () => e.PropertyName; // C# 3.0

I'm not a VB expert, but reflector claims this is the same as:

我不是VB专家,但反射器声称这与以下相同:

Dim t As Test = Function 
    Return e.PropertyName
End Function

Does that work?

那样有用吗?


Original answer:

You create delegates for properties with Delegate.CreateDelegate; this can be open for any instance of the type, of fixed for a single instance - and can be for getter or setter; I'll give an example in C#...

您可以使用Delegate.CreateDelegate为属性创建委托;这可以对任何类型的实例打开,对于单个实例是固定的 - 并且可以用于getter或setter;我将在C#中给出一个例子......

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


I just create an helper with pretty good performance : http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html It don't use IL / Emit approach and it is very fast !

我只是创建了一个具有相当好性能的帮助程序:http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html它不使用IL / Emit方法,而且速度非常快!

Edit by oscilatingcretin 2015/10/23

由oscilatingcretin编辑2015/10/23

The source contains some casing issues and peculiar ="" that have to be removed. Before link rot sets in, I thought I'd post a cleaned-up version of the source for easy copy pasta, as well as an example of how to use it.

源包含一些套管问题,并且必须删除特殊的“”。在链接腐烂之前,我想我会发布一个清理版本的源码,以便于复制意大利面,以及如何使用它的示例。

Revised source

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; }
    }
}

Use it like this:

像这样用它:

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


Here is a C#/.NET 2.0 version of Marc Gravell's response:

这是Marc Gravell的C#/。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);
 }
}

Again, 'Delegate.CreateDelegate' is what is fundamental to this example.

同样,'Delegate.CreateDelegate'是这个例子的基础。

#4


This is good idea

这是个好主意

Test t = () => e.PropertyName; // C# 3.0

But take care if your are doing something like this:

但要注意你是否正在做这样的事情:

List<Func<int>> funcs = new List<Func<int>>();
foreach (var e in Collection)
   funcs.Add(new Func<int>(() => e.Property));

Calling this:

foreach(var f in funcs)
   f();

Will always return value of property of the last object in Collection

将始终返回Collection中最后一个对象的属性值

In this case you should call method:

在这种情况下,您应该调用方法:

foreach (var e in Collection)
   funcs.Add(new Func<int>(e.GetPropValue));

#5


Here's a C# example but all the types are the same:

这是一个C#示例,但所有类型都是相同的:

First create the interface(delegate). Remember, a method that you attach to your delegate must return the same type, and take the same parameters as your delegate's declaration. Don't define your delegate in the same scope as your event.

首先创建接口(委托)。请记住,附加到委托的方法必须返回相同的类型,并采用与委托声明相同的参数。不要在与事件相同的范围内定义委托。

public delegate void delgJournalBaseModified();        

Make an event based on the delegate:

根据代表制作活动:

public static class JournalBase {
    public static event delgJournalBaseModified evntJournalModified;
};

Define a method that can be tied to your event that has an interface identical to the delegate.

定义一个方法,该方法可以绑定到具有与委托相同的接口的事件。

void UpdateEntryList()
{
}

Tie the method to the event. The method is called when the event is fired. You can tie as many methods to your event. I don't know the limit. It's probably something crazy.

将方法绑定到事件。触发事件时调用该方法。您可以将多种方法绑定到事件中。我不知道极限。这可能是疯狂的事情。

 JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList);

What happens here is the method is added as a callback for your event. When the event is fired, your method(s) will be called.

这里发生的是该方法被添加为您的事件的回调。触发事件时,将调用您的方法。

Next we make a method that will fire the event when called:

接下来,我们创建一个方法,在调用时触发事件:

public static class JournalBase {
    public static  void JournalBase_Modified()
    {
    if (evntJournalModified != null)
        evntJournalModified();
    }
};

Then you simply call the method -- JournalBase_Modified() -- somewhere in your code and all methods tied to your event are called too, one after another.

然后你只需调用方法 - JournalBase_Modified() - 在你的代码中的某个地方,所有与你的事件相关的方法也会被一个接一个地调用。

#6


VB version:

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())