Reflection Emit: como converter a instância de atributo para CustomAttributeBuilder ou CustomAttributeData

Fiz uma class de gerador que constrói uma class de proxy baseada na interface que implementa a interface.

Veja minha postagem em Criar uma class de proxy com base na interface sem implementá-la .

Estou familiarizado com CustomAttributeData.GetCustomAttributes(MemberInfo target) , usei-o quando li os membros da Interface e consegui importá-los para o proxy.

Eu quero injetar atributos adicionais para a class gerada em tempo de execução. Estou pedindo instâncias de atributos para injetá-los no proxy.

Por exemplo:

Um desenvolvedor pode passar isso como um valor: new ObsoleteAttribute("Demo", true) , (ele tem um construtor vazio, mas as propriedades são somente leitura), e eu quero convertê-lo para:

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

Lembre-se, não posso dizer o que é dado.

Esta não é uma solução geral, mas funcionará se você estiver disposto a restringir os atributos que você suporta àqueles com construtores sem parâmetros e propriedades e campos de leitura / gravação.

 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()); } 

Para fazer uma solução geral, você poderia usar expressões. Isso é mais complicado, mas permitiria uma syntax como:

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

Analisando a expressão para extrair as informações do construtor e os parâmetros seriam a parte complexa, mas isso pode ser feito.

 CustomAttributeBuilder BuildCustomAttribute(Expression exp) { //extract ConstructorInfo from exp //extract ParameterValues from exp //extract Attribute Type from exp return new CustomAttributeBuilder(ConstructorInfo, ParameterValues); } 

Obrigado Joe,
Eu encontrei a solução Expression no Attribute Builder , graças à sua input.
Estou disposto a trabalhar um pouco mais agora para tornar outros desenvolvedores mais fáceis de usar o meu proxy .

Eu esperava que poderia ser mais fácil, e se eu tenho a instância do atributo, por que não posso usá-lo como está e aplicar o atributo?

Se você tiver uma solução sem Expression , adoraria saber disso.

Aqui está a minha solução com o Expression baseado no Attribute Builder :

 private CustomAttributeBuilder GetCustumeAttributeBuilder(Expression> attributeExpression) { ConstructorInfo constructor = null; List constructorArgs = new List(); List namedProperties = new List(); List propertyValues = new List(); List namedFields = new List(); List fieldValues = new List(); 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 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 constructorArgs) { foreach (Expression arg in expression.Arguments) { LambdaExpression lambda = Expression.Lambda(arg); object value = lambda.Compile().DynamicInvoke(); constructorArgs.Add(value); } return expression.Constructor; } 

Se eu entendi a pergunta corretamente, isso deve adicionar um atributo personalizado ao tipo gerado

 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]));