web-dev-qa-db-ja.com

.NETプロパティのデリゲートを作成するにはどうすればよいですか?

私は(テストとして)デリゲートを作成しようとしています:

Public Overridable ReadOnly Property PropertyName() As String

私の直感的な試みは、次のようにデリゲートを宣言することでした。

Public Delegate Function Test() As String

そして、このようにインスタンス化します:

Dim t As Test = AddressOf e.PropertyName

しかし、これはエラーをスローします:

メソッド 'Public Overridable ReadOnly Property PropertyName()As String'には、デリゲート 'Delegate Function Test()AsString'と互換性のある署名がありません。

それで、私は不動産を扱っていたので、これを試しました:

Public Delegate Property Test() As String

しかし、これはコンパイラエラーをスローします。

だから問題は、どうすればプロパティのデリゲートを作成するのですか?


このリンクを参照してください:

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

21
Matt Mitchell

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());
    }
}
37
Marc Gravell

かなり良いパフォーマンスのヘルパーを作成するだけです: 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)));
}
12
user478450

これがC#/。NET 2.0バージョンの Marc Gravellの応答

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
Ray Vega

これは良い考えです

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));
2
vajanko

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

これは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()-を呼び出すだけで、イベントに関連付けられているすべてのメソッドも次々に呼び出されます。

0
Will