2013-04-09 43 views
5

我做了一個生成器類,它構建了一個基於實現接口的接口的代理類。反射發射:如何將屬性實例轉換爲CustomAttributeBuilder或CustomAttributeData

看到我的帖子在Build a Proxy class based on Interface without implementing it

我熟悉CustomAttributeData.GetCustomAttributes(MemberInfo target),我在閱讀Interface的成員時使用它,併成功將它們導入代理。

我想在運行時注入附加的屬性給生成的類。 我要求屬性實例將它們注入代理。

例如:

開發人員可以通過這個作爲值:new ObsoleteAttribute("Demo", true),(它有一個空的構造,但屬性是隻讀),我想將其轉換爲:

return new CustomAttributeBuilder(
       attribute.GetType().GetConstructor(Type[] {typeof (string), typeof (bool)}), 
       new object[] {"Demo", true}, 
       new FieldInfo[0], 
       new object[0]); 

請記住,我不知道給出了什麼。

+0

你是問如何將屬性添加到已生成的類('Type'),或者你目前正在構建一個類('TypeBuilder') ? – svick 2013-04-09 11:15:14

+0

我目前正在構建它 – Ofir 2013-04-09 14:53:54

+2

對CustomAttributeBuilder構造函數重載有什麼特別困惑嗎?我會期待他們不言自明。 – kvb 2013-04-09 15:28:02

回答

4

這不是一般的解決方案,但會工作,如果您願意將您支持的屬性限制爲具有無參數構造函數的屬性以及讀/寫屬性和字段

CustomAttributeBuilder BuildCustomAttribute(System.Attribute attribute) 
{ 
    Type type = attribute.GetType(); 
    var constructor = type.GetConstructor(Type.EmptyTypes); 
    var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 
    var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); 

    var propertyValues = from p in properties 
         select p.GetValue(attribute, null); 
    var fieldValues = from f in fields 
         select f.GetValue(attribute); 

    return new CustomAttributeBuilder(constructor, 
            Type.EmptyTypes, 
            properties, 
            propertyValues.ToArray(), 
            fields, 
            fieldValues.ToArray()); 
} 

要做一個通用的解決方案,你可以使用表達式。這是比較複雜的,但將允許像語法:

BuildCustomAttribute(() => new ObsoleteAttribute("Demo", true)); 

解析表達式提取構造信息和參數將是複雜的一部分,但它可以做到的。

CustomAttributeBuilder BuildCustomAttribute(Expression<Action> exp) 
{ 
    //extract ConstructorInfo from exp 
    //extract ParameterValues from exp 
    //extract Attribute Type from exp 

    return new CustomAttributeBuilder(ConstructorInfo, ParameterValues); 
} 
+2

再次,這是很多工作,以減輕開發人員需要了解CustomAttributeBuilder如何工作.... – 2013-04-10 18:38:21

0

如果我理解正確的問題,這應在自定義屬性添加到生成的類型

public class CustomAttribute: System.Attribute 
{ 
    public CustomAttribute() 
    { 
    } 
} 

TypeBuilder typeBuilder = module.DefineType(...) 

....

typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(CustomAttribute).GetConstructor(Type.EmptyTypes), 
    Type.EmptyTypes, 
    new FieldInfo[0], 
    new object[0])); 
+0

當我知道要添加的屬性時,這很好。 我想讓開發者添加任何屬性。 – Ofir 2013-04-10 06:16:23

+1

我明白了。我敢肯定,你可以使用System.Reflection或System.Linq.Expressions編寫代碼,將「新的ObsoleteAttribute(」Demo「,true)」轉換爲CustomAttributeBuilder實例,但這引出了一個問題,「爲什麼?您可以輕鬆地將一組CustomAttributeBuilder實例傳遞到您的代理生成方法中。雖然CustomAttributeBuilder在語法上更加複雜,但您將相同的信息傳遞給您的代理構建器。而且,在一般情況下,執行轉換的代碼將變得非常複雜 - 爲什麼要增加這種複雜性? – 2013-04-10 18:27:53

1

謝謝喬,
我確實在Attribute Builder找到Expression解決,感謝您的輸入。
我現在願意努力一點,讓其他開發人員更容易使用我的Proxy

我希望它可以更容易,如果我有屬性實例,爲什麼我不能按原樣使用它並應用該屬性?

如果您有沒有Expression的解決方案,我很樂意聽到它。

這是我與Expression解決方案基於Attribute Builder

private CustomAttributeBuilder GetCustumeAttributeBuilder(Expression<Func<Attribute>> attributeExpression) 
{ 
    ConstructorInfo constructor = null; 
    List<object> constructorArgs = new List<object>(); 
    List<PropertyInfo> namedProperties = new List<PropertyInfo>(); 
    List<object> propertyValues = new List<object>(); 
    List<FieldInfo> namedFields = new List<FieldInfo>(); 
    List<object> fieldValues = new List<object>(); 

    switch (attributeExpression.Body.NodeType) 
    { 
     case ExpressionType.New: 
      constructor = GetConstructor((NewExpression)attributeExpression.Body, constructorArgs); 
      break; 
     case ExpressionType.MemberInit: 
      MemberInitExpression initExpression = (MemberInitExpression)attributeExpression.Body; 
      constructor = GetConstructor(initExpression.NewExpression, constructorArgs); 

      IEnumerable<MemberAssignment> bindings = from b in initExpression.Bindings 
                 where b.BindingType == MemberBindingType.Assignment 
                 select b as MemberAssignment; 

      foreach (MemberAssignment assignment in bindings) 
      { 
       LambdaExpression lambda = Expression.Lambda(assignment.Expression); 
       object value = lambda.Compile().DynamicInvoke(); 
       switch (assignment.Member.MemberType) 
       { 
        case MemberTypes.Field: 
         namedFields.Add((FieldInfo)assignment.Member); 
         fieldValues.Add(value); 
         break; 
        case MemberTypes.Property: 
         namedProperties.Add((PropertyInfo)assignment.Member); 
         propertyValues.Add(value); 
         break; 
       } 
      } 
      break; 
     default: 
      throw new ArgumentException("UnSupportedExpression", "attributeExpression"); 
    } 

    return new CustomAttributeBuilder(
     constructor, 
     constructorArgs.ToArray(), 
     namedProperties.ToArray(), 
     propertyValues.ToArray(), 
     namedFields.ToArray(), 
     fieldValues.ToArray()); 
} 

private ConstructorInfo GetConstructor(NewExpression expression, List<object> constructorArgs) 
{ 
    foreach (Expression arg in expression.Arguments) 
    { 
     LambdaExpression lambda = Expression.Lambda(arg); 
     object value = lambda.Compile().DynamicInvoke(); 
     constructorArgs.Add(value); 
    } 
    return expression.Constructor; 
}