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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/724143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:34:57  来源:igfitidea点击:

How do I create a delegate for a .NET property?

c#vb.netdelegates

提问by Matt Mitchell

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'.

方法“公共可覆盖只读属性 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

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

采纳答案by Marc Gravell

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 解决问题 - 如果您在编译时知道道具名称,则可以(至少在 C# 中)使用匿名方法 / 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());
    }
}

回答by Will

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()——在您的代码中的某处,并且所有与您的事件相关的方法也被一个接一个地调用。

回答by Ray

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' 是这个例子的基础。

回答by user478450

I just create an helper with pretty good performance : http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.htmlIt 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)));
}

回答by vajanko

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 lastobject in Collection

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

In this case you should call method:

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

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

回答by stentor

VB version:

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