Create a delegate from a property getter or setter method
Asked Answered
R

4

28

To create a delegate from a method you can use the compile type-safe syntax:

private int Method() { ... }

// and create the delegate to Method...
Func<int> d = Method;

A property is a wrapper around a getter and setter method, and I want to create a delegate to a property getter method. Something like

public int Prop { get; set; }

Func<int> d = Prop;
// or...
Func<int> d = Prop_get;

Which doesn't work, unfortunately. I have to create a separate lambda method, which seems unnecessary when the getter method matches the delegate signature anyway:

Func<int> d = () => Prop;

In order to use the delegate method directly, I have to use nasty reflection, which isn't compile type-safe:

// something like this, not tested...
MethodInfo m = GetType().GetProperty("Prop").GetGetMethod();
Func<int> d = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), m);

Is there any way of creating a delegate on a property getting method directly in a compile-safe way, similar to creating a delegate on a normal method at the top, without needing to use an intermediate lambda method?

Roundlet answered 12/4, 2010 at 11:4 Comment(6)
What's the matter with the lambda ?Lactone
It's an extra unnecessary method, and makes things more complicated than is needed, as the property getter already matches the delegate signature anywayRoundlet
I fail to see your point here : a lambda is not a class method, but one way to instantiate a delegate. Func<int> d = () => Prop means I want my delegate d to "point" to the accessorLactone
The performance overhead of the lambda is minimal to none. The delegate call itself is much slower than the property call which may even get inlined. I really don't see why it would things more complicated?Toxin
@Cedric: the lambda is implemented as a method on the containing class, and the delegate points to that compiler-generated methodRoundlet
@Steven: ay, the performance overhead is negligible, but it just seems...unnecessary, when you can point to the property method directly.Roundlet
B
10

As far as I can tell, you have already written down all "valid" variants. Since it isn't possible to explicitly address a getter or setter in normal code (without reflection, that is), I don't think that there is a way to do what you want.

Bugbane answered 12/4, 2010 at 11:9 Comment(2)
There's no way to get the compiler to get a MethodInfo for a property getter method using a direct reference in code? :(Roundlet
Well, the only I can think of which should work is via lambda and expression tree. But that also requires some helper code to analyze the expression tree. See msdn.microsoft.com/en-us/library/bb397951.aspx and msdn.microsoft.com/en-us/library/…Bugbane
E
5

Having spent several hours puzzling this out, here is a solution for when you need to make fast property accessors from another class. Such as if you need to write a cached property map for previously unknown classes that have no klnowledge of that CreateDelegate magic.

A simple innocent data class, such as this one:

public class DataClass
{
    public int SomeProp { get; set; }
    public DataClass(int value) => SomeProp = value;
}

The universal accessor class, where T1 is the type of class that contains a property and T2 is the type of that property looks like this:

public class PropAccessor<T1, T2>
{
    public readonly Func<T1, T2> Get;
    public readonly Action<T1, T2> Set;

    public PropAccessor(string propName)
    {
        Type t = typeof(T1);
        MethodInfo getter = t.GetMethod("get_" + propName);
        MethodInfo setter = t.GetMethod("set_" + propName);

        Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
        Set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
    }
}

And then you can do:

var data = new DataClass(100);

var accessor = new PropAccessor<DataClass, int>("SomeProp");

log(accessor.Get(data));
accessor.Set(data, 200);
log(accessor.Get(data));

Basically, you can traverse your classes with reflection at startup and make a cache of PropAccessors for each property, giving you reasonably fast access.

Edit: a few more hours later..

Ended up with something like this. The abstract ancestor to PropAccessor was necessary, so that I could actually declare a field of that type in the Prop class, without resorting to use of dynamic. Ended up approximately 10x faster than with MethodInfo.Invoke for getters and setters.

internal abstract class Accessor
{
    public abstract void MakeAccessors(PropertyInfo pi);
    public abstract object Get(object obj);
    public abstract void Set(object obj, object value);
}

internal class PropAccessor<T1, T2> : Accessor
{
    private Func<T1, T2>    _get;
    private Action<T1, T2>  _set;

    public override object Get(object obj) => _get((T1)obj);
    public override void Set(object obj, object value) => _set((T1)obj, (T2)value);

    public PropAccessor() { }

    public override void MakeAccessors(PropertyInfo pi)
    {
        _get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, pi.GetMethod);
        _set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, pi.SetMethod);
    }
}

internal class Prop
{
    public string name;
    public int length;
    public int offset;
    public PropType type;
    public Accessor accessor;
}

internal class PropMap
{
    public UInt16 length;
    public List<Prop> props;

    internal PropMap()
    {
        length = 0;
        props = new List<Prop>();
    }

    internal Prop Add(PropType propType, UInt16 size, PropertyInfo propInfo)
    {
        Prop p = new Prop()
        {
            name   = propInfo.Name,
            length = size,
            offset = this.length,
            type   = propType,
            Encode = encoder,
            Decode = decoder,
        };

        Type accessorType = typeof(PropAccessor<,>).MakeGenericType(propInfo.DeclaringType, propInfo.PropertyType);
        p.accessor = (Accessor)Activator.CreateInstance(accessorType);
        p.accessor.MakeAccessors(propInfo);

        this.length += size;
        props.Add(p);
        return p;
    }
}
Ebert answered 24/5, 2019 at 16:24 Comment(0)
I
2

The trick is that a Property is really just a facade on the actual getter and/or setter methods which are hidden. The compiler emits these method(s) and names them according to the name of the Property prepended with get_ and set_, respectively. In the example below it would be int get_Value() and void set_Value(int). So just bypass the so-called "property" and just go straight for those methods.

With either the getter and/or setter method we have two options.

  • We can create a bound delegate which has the this value for some instance "burned-in." This is similar to what you expect for the property itself, i.e., this delegate will only be good for accessing that one runtime instance. The advantage is that, because the delegate is permanently bound to its instance, you don't have to pass in an extra argument.

  • The other option is to create delegates which aren't associated with a specific target instance. Although these call the exact same property-accessor methods as before, in this case the Target property of the delegate itself is empty/null. Lacking any this pointer to use, the method signature for an unbound delegate is altered to reveal the famous "hidden this" pointer.

Further discussion below, but first here's the code. It illustrates all the four cases, getter/setter -vs- bound/unbound.

partial class Cls
{
    static Cls()
    {
        UnboundGet = create<Func<Cls, int>>(null, mi_get);
        UnboundSet = create<Action<Cls, int>>(null, mi_set);
    }

    public Cls()
    {
        BoundGet = create<Func<int>>(this, mi_get);
        BoundSet = create<Action<int>>(this, mi_set);
    }

    public readonly static Func<Cls, int> UnboundGet;
    public readonly static Action<Cls, int> UnboundSet;

    public readonly Func<int> BoundGet;
    public readonly Action<int> BoundSet;

    public int Value { get; set; }
};

n.b., this refers to some helper code that's included at the bottom of this post

To summarize, the "true signature" of the instance method is identical to the bound delegate case, but gets cancelled off. Bound delegates take care of providing it, as the first argument, by supplying the instance they carry around in that Target property. Unbound delegates are universal so you never need more than just a single getter/setter pair per-property. They can be used to to access that instance property on any past, present, or future runtime instance, but this means you have to explicitly pass a desired target this object in as the first argument every time you invoke the getter/setter.

Note also that even though unbound delegates here are accessing instance properties or methods, you don't actually need any viable runtime instance of Cls to create the delegate.


Here's a demo.

static class demo
{
    static demo()
    {
        var c1 = new Cls { Value = 111 };
        var c2 = new Cls { Value = 222 };

        Console.WriteLine("c1: {0}  c2: {1}", c1, c2);

        c1.BoundSet(c1.Value + 444);
        Cls.UnboundSet(c2, c2.BoundGet() + 444);

        Console.WriteLine("c1: {0}  c2: {1}", c1, c2);
    }
};

And the output:

c1: 111 111 111  c2: 222 222 222
c1: 555 555 555  c2: 666 666 666

Finally, here's some helper stuff I put down here to reduce clutter. Note that the MethodInfos can be cached and reused if you plan on building lots of bound delegates. If you instead prefer to use the unbound (static) delegates, you won't need to keep them around; because unbound delegates work universally for any instance, so you might decide you never need to create any bound delegates.

partial class Cls
{
    static MethodInfo mi_get = typeof(Cls).GetMethod("get_Value"),
                      mi_set = typeof(Cls).GetMethod("set_Value");

    static T create<T>(Object _this, MethodInfo mi) =>
        (T)(Object)Delegate.CreateDelegate(typeof(T), _this, mi);

    public override String ToString() =>
            String.Format("{0} {1} {2}", Value, BoundGet(), Cls.UnboundGet(this));
}
Inexistent answered 1/4, 2018 at 0:24 Comment(0)
H
0

Another option (in .NET 3.0 and newer) would be to use a DependencyProperty instead of a traditional property. Then you can pass around the DependencyProperty object (instead of passing around a delegate), and call GetValue() or SetValue() when needed.

(Yes, I know this is an old question, but it was one of the top posts when I was trying to do something very similar.)

Howard answered 29/9, 2015 at 19:47 Comment(1)
I would have upvoted this, if it showed code snippets for 1) Declaring a DependencyProperty and 2) Using that dependency property to accomplish the desired goal. However, while this is a good technique to have in mind, I don't think a DependencyProperty helps for the "typical" goal as shown in the question: there is an existing method (perhaps in some library) which needs a method with the same signature as your getter or setter. That is, if you start using a DependencyProperty, then you'll have to use it in that called method, and all callers will have to pass in such a property.Aciniform

© 2022 - 2024 — McMap. All rights reserved.