2013-04-09 2 views
5

Я сделал класс генератора, который строит прокси-класс на основе интерфейса, реализующего интерфейс.Reflection Emit: как преобразовать экземпляр атрибута в CustomAttributeBuilder или CustomAttributeData

Посмотреть мое сообщение на Build a Proxy class based on Interface without implementing it.

Я знаком с CustomAttributeData.GetCustomAttributes(MemberInfo target), я использовал его, когда читал члены интерфейса и им удалось импортировать их в прокси.

Я хочу добавить дополнительные атрибуты сгенерированного класса во время выполнения. Я прошу экземпляры атрибутов ввести их в прокси.

Например:

Разработчик может передать это в качестве значения: 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

Это не общее решение, но будет работать, если вы готовы ограничить атрибуты, которые вы поддерживаете тем, у кого нет конструкторов без параметров, и свойства и поля чтения/записи

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

Спасибо Джо,
я нашел Expression решения в Attribute Builder, благодаря вашему входу.
Теперь я готов работать немного сложнее, чтобы другие разработчики упростили использование моего 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; 
} 
Смежные вопросы