我目前正在构建节点编辑器(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>.GetValue
和NodeInput<T>.SetValue
定义如下:
public delegate T GetValue();
public delegate void SetValue(T value);
...分别在NodeOutput
和NodeInput
。
有没有人有过为属性访问器创建委托的经验?当有问题的类型是通用的时,不知道它会有什么不同吗?
答案 0 :(得分:1)
要创建属性访问者的委托,只需使用GetGetMethod
和GetSetMethod
;你能解释它在哪里发生故障吗?
简化示例:
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之类的内容;类似的性能,但更简单,因为您只需使用PropertyDescriptor
和object
(启用后):
var prop = TypeDescriptor.GetProperties(obj)["Value"];
object val = prop.GetValue(prop);
prop.SetValue(prop, 321);
最后一个选项是Expression
;我在this series的Expression
中介绍了各种属性访问技巧。
答案 1 :(得分:1)
我认为你的类型不匹配。在第一个异常行中,setter
被声明为类型NodeInput<T>
,这意味着它是一个接受T并返回void的方法。但是您分配给setter
的方法是property.GetSetMethod(),它将是一个采用property.PropertyType并返回void的方法。这将导致异常,除非幸运的是property.PropertyType与T相同。类似于第二个异常行上的getter
。
我怀疑你无法使用泛型来处理它,因为你在编译时不知道property.PropertyType,所以你不能将该类型作为泛型参数传递(因为必须在编译时指定泛型参数,除非你使用type.MakeGenericType)。