如何从泛型类型获取属性访问器的委托?

时间:2009-10-17 08:04:06

标签: c# generics reflection properties delegates

我目前正在构建节点编辑器(as in Blender),并且无法从泛型类型获取属性访问器的委托。到目前为止,问题here使我最接近,但我遇到麻烦,我认为这与通用对象的类型有关。

作为参考,“节点”与对象同义,“端口”与属性同义。

这是违规代码,它是Node类的一部分。 NodePort类是可以在属性上设置的属性,用于为属性提供详细信息(例如人类可读的名称和数据流方向)。

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>.GetValueNodeInput<T>.SetValue定义如下:

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

...分别在NodeOutputNodeInput

有没有人有过为属性访问器创建委托的经验?当有问题的类型是通用的时,不知道它会有什么不同吗?

2 个答案:

答案 0 :(得分:1)

要创建属性访问者的委托,只需使用GetGetMethodGetSetMethod;你能解释它在哪里发生故障吗?

简化示例:

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);
    }
}

请注意,要处理object上的内容,有一些涉及基类等的技巧 - 或者可能会考虑HyperDescriptor之类的内容;类似的性能,但更简单,因为您只需使用PropertyDescriptorobject(启用后):

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

最后一个选项是Expression;我在this seriesExpression中介绍了各种属性访问技巧。

答案 1 :(得分:1)

我认为你的类型不匹配。在第一个异常行中,setter被声明为类型NodeInput<T>,这意味着它是一个接受T并返回void的方法。但是您分配给setter的方法是property.GetSetMethod(),它将是一个采用property.PropertyType并返回void的方法。这将导致异常,除非幸运的是property.PropertyType与T相同。类似于第二个异常行上的getter

我怀疑你无法使用泛型来处理它,因为你在编译时不知道property.PropertyType,所以你不能将该类型作为泛型参数传递(因为必须在编译时指定泛型参数,除非你使用type.MakeGenericType)。