2009-10-17 40 views
2

我目前正在构建一个节点编辑器(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

有没有人有任何经验与财产访问者创建代表?任何想法如何可能会有所不同,当问题的类型是通用的?

+0

任何类型的属性都可以用* NodePort *来装饰吗? – Elisha 2009-10-17 08:36:16

+0

是的。 'NodePort'可以应用于任何属性。 – 2009-10-17 08:56:55

回答

1

我想你在这里有一个类型不匹配。在您的第一条异常行中,setter被声明为NodeInput<T>,这意味着它是一个接受T并返回void的方法。但是您分配给setter的方法是property.GetSetMethod(),它将是一个接受property.PropertyType并返回void的方法。这将导致一个异常,除非通过好运财产。属性类型与T相同。类似地,对于第二个异常行中的getter

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

+0

这应该是Delegate.CreateDelegate的目的 - 它接受一个类型(typeof(NodeInput .GetSetter)),一个目标(Target)和一个MethodInfo(property.GetSetMethod())。 GetSetMethod()的MethodInfo只是描述了一个简单的setter。无效设置(T)。 http://msdn.microsoft.com/en-us/library/system.reflection.propertyinfo.getsetmethod(VS.71)。aspx – 2009-10-17 08:49:52

+1

但是GetSetMethod的MethodInfo与void Set(T)不匹配。它匹配void Set(property.PropertyType)。将property.GetSetMethod捕获到一个临时变量中,并在调试器中对其进行检查。例如,假设T有一个int类型的属性Foo。然后,在你的循环中,属性是PropertyOfforFoo,property.GetSetMethod将是int => void类型,而不是T => void类型,因为Foo属性设置器接受一个int而不是T. set方法是*在T上声明*,但不接受* T。 – itowlson 2009-10-17 09:07:54

+0

好的,我明白了,是的,你是对的。所以我想现在的问题是,我如何从System.Type实例化一个通用对象?我有属性的System.Type,但我不知道如何使用它来创建一个通用的实例(在这种情况下,我的代理设置器)。 – 2009-10-17 09:39:30

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;我覆盖Expressionthis series的各种财产访问技巧。