2011-03-14 37 views
2

我在說類似於dynamic的東西。 This沒有回答我的問題,所以這個問題。我想有一個可以在運行時添加屬性的類。它需要從object類型繼承。C#動態類

我見過從DynamicObject繼承,但沒有說明如何在運行時添加屬性。對我來說,能否給我一些啓示?

我有這樣一個類:

public class SomeModel : DynamicObject { 
     public string SomeMandatoryProperty {get; set;} 
} 

我想從另一個類在運行時添加的所有屬性這一類。所以例如。

SomeModel m = new SomeModel(); 
m = someOtherClass; 
string hi = m.otherClassProp; //Property from other class is added. 
string mandatory = m.SomeMandatoryProperty; //From the mandatory property set previously. 
+0

對上一個問題的回答是最好的,你可以得到。你爲什麼不喜歡它? – Jon

+0

它使用T4模板來生成源代碼。那個人特意說他不是在說「動態」之類的東西,我想要「動態」之類的東西。 –

回答

1

你想使用的ExpandoObject因爲您可以根據需要動態地添加屬性。然而,沒有直接的方法可以很容易地用另一個對象的值填充實例。您必須使用反射手動添加它。

你想編寫一個包裝器對象,你可以添加屬性,同時仍然訪問內部?您可能想要這樣認爲,您不必在兩個不同的對象實例之間管理兩個值的副本。我編寫了一個測試類來包裝字符串對象,以演示如何做到這一點(類似於ExpandoObject的工作原理)。它應該給你一個關於如何爲你的類型做這件事的想法。

class DynamicString : DynamicObject 
{ 
    static readonly Type strType = typeof(string); 
    private string instance; 
    private Dictionary<string, object> dynProperties; 
    public DynamicString(string instance) 
    { 
     this.instance = instance; 
     dynProperties = new Dictionary<string, object>(); 
    } 
    public string GetPrefixString(string prefix) 
    { 
     return String.Concat(prefix, instance); 
    } 
    public string GetSuffixString(string suffix) 
    { 
     return String.Concat(instance, suffix); 
    } 
    public override string ToString() 
    { 
     return instance; 
    } 
    public override bool TryConvert(ConvertBinder binder, out object result) 
    { 
     if (binder.Type != typeof(string)) 
      return base.TryConvert(binder, out result); 
     result = instance; 
     return true; 
    } 
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
    { 
     var method = strType.GetMethod(binder.Name, args.Select(a => a.GetType()).ToArray()); 
     if (method == null) 
     { 
      result = null; 
      return false; 
     } 
     result = method.Invoke(instance, args); 
     return true; 
    } 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     var members = strType.GetMember(binder.Name); 
     if (members.Length > 0) 
     { 
      var member = members.Single(); 
      switch (member.MemberType) 
      { 
      case MemberTypes.Property: 
       result = ((PropertyInfo)member).GetValue(instance, null); 
       return true; 
       break; 
      case MemberTypes.Field: 
       result = ((FieldInfo)member).GetValue(instance); 
       return true; 
       break; 
      } 
     } 
     return dynProperties.TryGetValue(binder.Name, out result); 
    } 
    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     var ret = base.TrySetMember(binder, value); 
     if (ret) return true; 
     dynProperties[binder.Name] = value; 
     return true; 
    } 
} 

如果您想要冒險,可以定義自己的元對象來處理綁定。你可能會得到不同類型的可重用元對象,並極大地簡化你的代碼。我已經玩了一段時間了,到目前爲止還有這個。它不處理動態添加屬性。我不會再做這個工作,但我會把它留在這裏作爲參考。

class DynamicString : DynamicObject 
{ 
    class DynamicStringMetaObject : DynamicMetaObject 
    { 
     public DynamicStringMetaObject(Expression parameter, object value) 
      : base(parameter, BindingRestrictions.Empty, value) 
     { 
     } 
     public override DynamicMetaObject BindConvert(ConvertBinder binder) 
     { 
      if (binder.Type == typeof(string)) 
      { 
       var valueType = Value.GetType(); 
       return new DynamicMetaObject(
        Expression.MakeMemberAccess(
         Expression.Convert(Expression, valueType), 
         valueType.GetProperty("Instance")), 
        BindingRestrictions.GetTypeRestriction(Expression, valueType)); 
      } 
      return base.BindConvert(binder); 
     } 
     public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 
     { 
      System.Diagnostics.Trace.WriteLine(String.Format("BindGetMember: {0}", binder.Name)); 
      var valueType = Value.GetType(); 
      var self = Expression.Convert(Expression, valueType); 
      var valueMembers = valueType.GetMember(binder.Name); 
      if (valueMembers.Length > 0) 
      { 
       return BindGetMember(self, valueMembers.Single()); 
      } 
      var members = typeof(string).GetMember(binder.Name); 
      if (members.Length > 0) 
      { 
       var instance = 
        Expression.MakeMemberAccess(
         self, 
         valueType.GetProperty("Instance")); 
       return BindGetMember(instance, members.Single()); 
      } 
      return base.BindGetMember(binder); 
     } 
     private DynamicMetaObject BindGetMember(Expression instance, MemberInfo member) 
     { 
      return new DynamicMetaObject(
       Expression.Convert(
        Expression.MakeMemberAccess(instance, member), 
        typeof(object)), 
       BindingRestrictions.GetTypeRestriction(Expression, Value.GetType()) 
      ); 
     } 
    } 
    public string Instance { get; private set; } 
    public DynamicString(string instance) 
    { 
     Instance = instance; 
    } 
    public override DynamicMetaObject GetMetaObject(Expression parameter) 
    { 
     return new DynamicStringMetaObject(parameter, this); 
    } 
    public override string ToString() 
    { 
     return Instance; 
    } 
    public string GetPrefixString(string prefix) 
    { 
     return String.Concat(prefix, Instance); 
    } 
    public string GetSuffixString(string suffix) 
    { 
     return String.Concat(Instance, suffix); 
    } 
} 
+0

我假設這將是非常緩慢,如果我轉換成千上萬的對象。對? –

+0

@Lol:如果使用包裝對象,它將比使用ExpandoObject快得多,因爲您不是複製值,而是將所有事物委託給包裝對象。在此之後,兩者都應該以類似的方式運作。是的,由於涉及反射,使用動態類型會相對較慢。此時,您必須決定是否要在複製周圍數據或使用反射時支付(速度)。有了成千上萬的物體,我會堅持反思。 –

2

您應該可以使用ExpandoObject來代替。一個ExpandoObject可以有成員加入或在運行時取下,如果你要轉換爲XML等

從MSDN文檔中有非常好的支持:

dynamic employee, manager; 

    employee = new ExpandoObject(); 
    employee.Name = "John Smith"; 
    employee.Age = 33; 

    manager = new ExpandoObject(); 
    manager.Name = "Allison Brown"; 
    manager.Age = 42; 
    manager.TeamSize = 10; 
3

我認爲你正在尋找ExpandoObject

ExpandoObject類使您可以在運行時 實例添加和刪除其實例的成員,也可以設置 並獲取這些成員的值。這個 類支持動態綁定,其中 使您能夠使用標準語法 ,如sampleObject.sampleMember,而不是 的更復雜的語法,如 sampleObject.GetAttribute(「sampleMember」)。

dynamic manager; 

manager = new ExpandoObject(); 
manager.Name = "Allison Brown"; 
manager.Age = 42; 
manager.TeamSize = 10; 
+0

有沒有辦法將另一個類的屬性合併到這個? –

+0

你可以使用反射來做到這一點,我不知道任何其他更直接的方式。 – BrokenGlass