【问题标题】:How do I create a delegate for a .NET property?如何为 .NET 属性创建委托?
【发布时间】:2009-04-07 04:30:36
【问题描述】:

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

Public Overridable ReadOnly Property PropertyName() As String

我的直觉尝试是这样声明委托:

Public Delegate Function Test() As String

并像这样实例化:

Dim t As Test = AddressOf e.PropertyName

但这会引发错误:

Method 'Public Overridable ReadOnly Property PropertyName() As String' 没有签名 兼容委托'委托 函数 Test() As String'。

所以因为我正在处理一个属性,所以我尝试了这个:

Public Delegate Property Test() As String

但这会引发编译器错误。

所以问题是,我如何为一个属性创建一个委托?


查看此链接:

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

【问题讨论】:

    标签: c# vb.net delegates


    【解决方案1】:

    使用 AddressOf 解决问题 - 如果您在编译时知道 prop-name,则可以(至少在 C# 中)使用 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;我将在 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());
        }
    }
    

    【讨论】:

    • 谢谢 - 我为一个有问题的项目卡在 .NET 2.0 中,我会在这里看看是否有类似的工作和反馈(否则可能是我链接到的复杂解决方案出现的原因)
    • 它似乎可以工作(尚未经过广泛测试),但我想知道您是否可以帮助解决这个问题。我需要在不使用硬编码字符串的情况下获取该属性。问题是,我需要 PropertyInfo 来获取 get 方法,而我无法从属性 addressOf 中获取它
    • 感谢您的更新-不幸的是 VB.NET 不支持匿名方法-我想唯一的解决方案是为每个属性创建一个函数包装器并为此创建一个委托(几乎是反射器代码正在做)。
    • VB.NET 支持 lambda 表达式(但它们必须返回一个值) - blogs.msdn.com/wriju/archive/2008/02/05/…
    • 需要注意的是,Lambda 直到 VB2008 才存在于 VB 中,即便如此,我相信在 VB2010 之前它们也只是单行代码。
    【解决方案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)));
    }
    

    【讨论】:

    • 你的这个属性访问器太棒了!我的新应用程序基于您的属性访问器,在修复了一些小错误后,一切都像魅力一样工作。
    • 在当前状态下,这对 SO 来说不是很好的答案 - 应该是评论或扩展...基于 (10K+) 已删除的问题stackoverflow.com/questions/33292378/for-generic-parameters 问题,链接代码有可能需要返工可用(由于许可问题,可能不会复制到 SO)。
    • @AlexeiLevenkov 您链接到的问题已被删除。此外,更多地扩展许可问题。回答者链接到他们的博客,他们在其中提供了代码示例。当然,您不知道它是否需要许可才能使用,但您怎么知道发布到 SE 的任何代码示例都不需要许可?
    • 我还要补充一点,我已经在那里测试了代码,而且速度非常快。它甚至可以与我一直使用的表达式树方法并驾齐驱。但是,您必须编辑代码才能使其正常工作。有大小写问题,还有这些特殊的="" 需要删除。我发现这段代码在 5 年后仍然有效,这让我印象深刻。
    • @oscilatingcretin - 所以对所有发布的内容都有非常具体的许可 - stackoverflow.com/help/licensing - CC BY-SA 3.0,重新发布在不同许可下制作的其他内容通常是非常有问题的。我知道大多数人不在乎,但问题仍然存在。
    【解决方案3】:

    这是Marc Gravell's response的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);
     }
    }
    

    同样,'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();
      

      将始终返回 Collection 中 last 对象的属性值

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

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

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-18
          • 2012-06-04
          • 1970-01-01
          • 2020-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-19
          相关资源
          最近更新 更多