2013-04-09 6 views
5

私は、インターフェイスを実装するインターフェイスに基づいてプロキシクラスを作成するジェネレータクラスを作成しました。Reflection Emit:属性インスタンスを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

+0

私は現在それを構築しています – Ofir

+2

'CustomAttributeBuilder'コンストラクタのオーバーロードに関して特に混乱しているものはありますか?私は彼らが自明であると思ったでしょう。 – kvb

答えて

4

にカスタム属性を追加する必要があります。これは、一般的な解決策ではありませんが、あれば動作しますパラメータのないコンストラクタとRead/Writeのプロパティとフィールドを持つものにサポートする属性を拘束します。

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の仕組みを理解する必要性を軽減するための多くの作業です。 –

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

+1

私は参照してください。私はSystem.ReflectionまたはSystem.Linq.Expressionsを使って "新しいObsoleteAttribute(" Demo "、true)をCustomAttributeBuilderインスタンスに変換できるコードを書くことができると確信していますが、それはなぜですか? CustomAttributeBuilderインスタンスの配列をプロキシ生成メソッドに簡単に渡すこともできます。 CustomAttributeBuilderは構文的に複雑ですが、同じ情報をプロキシビルダーに渡しています。また、変換を行うコードは、一般的なケースでは非常に複雑になるでしょう - なぜその複雑さを追加するのですか? –

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; 
} 
関連する問題