2009-10-17 76 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的各種財產訪問技巧。