2013-04-09 35 views
5

Tôi đã tạo một lớp máy phát điện để xây dựng một lớp proxy dựa trên giao diện thực hiện giao diện.Phản xạ Emit: làm thế nào để chuyển đổi thể hiện thuộc tính thành CustomAttributeBuilder hoặc CustomAttributeData

Xem bài đăng của tôi trên Build a Proxy class based on Interface without implementing it.

Tôi quen thuộc với CustomAttributeData.GetCustomAttributes(MemberInfo target), tôi đã sử dụng nó khi đọc thành viên của Giao diện và nhập thành công vào proxy.

Tôi muốn tiêm bổ sung thuộc tính vào lớp được tạo trong thời gian chạy. Tôi yêu cầu các trường hợp thuộc tính để tiêm chúng vào proxy.

Ví dụ:

Một nhà phát triển có thể chuyển thông tin này như một giá trị: new ObsoleteAttribute("Demo", true), (nó có một constructor rỗng, nhưng thuộc tính này được chỉ đọc), và tôi muốn chuyển nó sang:

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

Hãy nhớ rằng, tôi không thể nói những gì được đưa ra.

+0

Có phải bạn hỏi làm thế nào để thêm các thuộc tính cho lớp đã được tạo ra ('Type') hoặc một lớp học mà bạn đang xây dựng (' TypeBuilder') ? – svick

+0

Tôi hiện đang xây dựng nó – Ofir

+2

Có điều gì đặc biệt khó hiểu về quá tải của hàm tạo 'CustomAttributeBuilder' không? Tôi đã mong đợi họ sẽ tự giải thích. – kvb

Trả lời

4

Đây không phải là một giải pháp chung, nhưng sẽ có tác dụng nếu bạn sẵn sàng hạn chế các thuộc tính mà bạn hỗ trợ cho những người có các nhà xây dựng không có tham số và các thuộc tính Read/Write và Fields

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

Để làm một giải pháp chung, bạn có thể sử dụng các biểu thức. Điều đó phức tạp hơn, nhưng sẽ cho phép cú pháp như:

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

Phân tích cú pháp biểu thức để trích xuất thông tin của hàm tạo và tham số sẽ là một phần phức tạp, nhưng có thể thực hiện được.

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

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

Một lần nữa, đây là rất nhiều công việc để làm giảm các nhà phát triển của bạn về sự cần thiết phải hiểu cách CustomAttributeBuilder hoạt động .... –

0

Nếu tôi hiểu câu hỏi một cách chính xác, điều này sẽ thêm một thuộc tính tùy chỉnh để tạo ra các loại

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

Điều này thật tuyệt khi tôi biết thuộc tính cần thêm vào. Tôi muốn cho phép nhà phát triển thêm bất kỳ thuộc tính nào. – Ofir

+1

Tôi hiểu. Tôi chắc rằng bạn có thể viết mã bằng cách sử dụng System.Reflection hoặc System.Linq.Expressions có thể chuyển đổi "ObsoleteAttribute mới (" Demo ", true)" thành một thể hiện CustomAttributeBuilder, nhưng điều đó đặt ra câu hỏi, "tại sao?" Bạn có thể dễ dàng truyền một mảng các cá thể CustomAttributeBuilder vào phương thức tạo proxy của bạn. Mặc dù CustomAttributeBuilder phức tạp hơn về cú pháp, nhưng bạn đang truyền thông tin tương tự cho trình tạo proxy của mình. Ngoài ra, mã để thực hiện chuyển đổi sẽ trở nên rất phức tạp trong trường hợp chung - tại sao lại thêm độ phức tạp đó? –

1

Cảm ơn bạn Joe,
tôi đã tìm ra giải pháp Expression tại Attribute Builder, nhờ vào đầu vào của bạn.
Tôi sẵn sàng làm việc chăm chỉ hơn một chút để làm cho các nhà phát triển khác dễ sử dụng số Proxy của tôi.

Tôi hy vọng nó có thể dễ dàng hơn và nếu tôi có thể hiện thuộc tính, tại sao tôi không thể sử dụng nó như vậy và áp dụng thuộc tính?

Nếu bạn có giải pháp không có Expression, tôi rất muốn nghe về điều đó.

Đây là giải pháp của tôi với Expression dựa trên 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; 
}