开发者

How can I get delegates to property accessors from a generic type?

开发者 https://www.devze.com 2022-12-08 14:23 出处:网络
I\'m currently building a node editor (as in Blender) and am having trouble getting delegates to property accessors from a generic type. So far the question here has brought me closest, but I\'m havin

I'm currently building a node editor (as in Blender) and am having trouble getting delegates to property accessors from a generic type. So far the question here has brought me closest, but I'm having trouble that I think is specifically related to the type of object being generic.

For reference, a "Node" is synonymous with an object, and a "Port" is synonymous with a property.

This is the offending code, which is part of the Node class. The NodePort class is an attribute that may be set on properties to provide details (such as the human-readable name, and data-flow direction) for the property.

public void SetTarget<T>(T Target)
{
    //TODO: Finish clearing old IOs (if any)
    Inputs.Clear();
    Outputs.Clear();

    //Keep track of the current target of this node.
    ThisTarget = Target;

    PropertyInfo[] pinfo = Target.GetType().GetProperties();

    foreach (PropertyInfo property in pinfo)
    {
        Attribute[] attrs = Attribute.GetCustomAttributes(property);
        foreach (Attribute attribute in attrs)
        {
            // If the property has a NodePort attribute, it's specifically requesting to be available as a port on the node.
            if (attribute is NodePort)
            {
                NodePort PortDetails = (NodePort)attribute;

                if (PortDetails.Direction == NodePort.NodePortDirection.PORT_INPUT)
                {
                    // This line throws an ArgumentException, and the only message is "Error binding to target method."
                    NodeInput<T>.SetValue Setter = (NodeInput<T>.SetValue)Delegate.CreateDelegate(typeof(NodeInput<T>.SetValue), (T)Target, property.GetSetMethod());
                    AddInput(Setter, PortDetails.CommonName);
                }
                else if (PortDetails.Direction == NodePort.NodePortDirection.PORT_OUTPUT)
                {
                    // Same exception here.
                    NodeOutput<T>.GetValue Getter = (NodeOutput<T>.GetValue)Delegate.CreateDelegate(typeof(NodeOutput<T>.GetValue), (T)Target, property.GetGetMethod());
                    AddOutput(Getter, PortDetails.CommonName);
                }
            }
        }

    }
}

NodeOutput<T>.GetValue and NodeInput<T>.SetValue are defined as such:

public delegate T GetValue();
public delegate void SetValue(T value);

...in NodeOutput and NodeInput respectively.

Does anyone have any experience with creating delegates for property accessors? Any idea how it may be different when the type in question 开发者_如何转开发is generic?


I think you have a type mismatch here. In the first of your exception lines, setter is declared to be of type NodeInput<T>, which means it is a method that takes T and returns void. But the method that you are assigning to setter is property.GetSetMethod(), which will be a method that takes property.PropertyType and returns void. This will cause an exception unless by good fortune the property.PropertyType is the same as T. Similarly for getter on the second of your exception lines.

I suspect you can't handle this using generics, because you don't know property.PropertyType at compile time, so you can't pass that type as a generic parameter (because generic parameters must be specified at compile time, unless you use type.MakeGenericType).


To create a delegate to the property accessors, you just use GetGetMethod and GetSetMethod; can you explain where it is breaking down?

A simplified example:

using System;
class Foo<T>
{
    public T Value { get; set; }
}
static class Program
{
    static void Main()
    {
        var obj = new Foo<int> { Value = 123 };
        var prop = obj.GetType().GetProperty("Value");
        Func<Foo<int>, int> getter = (Func<Foo<int>, int>)
            Delegate.CreateDelegate(
              typeof(Func<Foo<int>, int>), prop.GetGetMethod());
        int x = getter(obj);
        Console.WriteLine(x);
        Action<Foo<int>, int> setter = (Action<Foo<int>, int>)
            Delegate.CreateDelegate(
              typeof(Action<Foo<int>, int>), prop.GetSetMethod());
        setter(obj, 321);
        Console.WriteLine(obj.Value);
    }
}

Note that to handle things just on object there are tricks involving base-classes etc - or perhaps consider things like HyperDescriptor; similar performance, but much simpler, since you just use PropertyDescriptor and object (after enabling it):

var prop = TypeDescriptor.GetProperties(obj)["Value"];
object val = prop.GetValue(prop);
prop.SetValue(prop, 321);

A last option is Expression; I cover various tricks for property access in Expression in this series.

0

精彩评论

暂无评论...
验证码 换一张
取 消