Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expression in attribute constructor

I have created an Attribute class called RelatedPropertyAttribute:

[AttributeUsage(AttributeTargets.Property)] public class RelatedPropertyAttribute: Attribute {     public string RelatedProperty { get; private set; }      public RelatedPropertyAttribute(string relatedProperty)     {         RelatedProperty = relatedProperty;     } } 

I use this to indicate related properties in a class. Example of how I would use it:

public class MyClass {     public int EmployeeID { get; set; }      [RelatedProperty("EmployeeID")]     public int EmployeeNumber { get; set; } } 

I would like to use lambda expressions so that I can pass a strong type into my attribute's constructor, and not a "magic string". This way I can exploit compiler type checking. For example:

public class MyClass {     public int EmployeeID { get; set; }      [RelatedProperty(x => x.EmployeeID)]     public int EmployeeNumber { get; set; } } 

I thought I could do it with the following, but it isn't allowed by the compiler:

public RelatedPropertyAttribute<TProperty>(Expression<Func<MyClass, TProperty>> propertyExpression) { ... } 

Error:

The non-generic type 'RelatedPropertyAttribute' cannot be used with type arguments

How can I achieve this?

like image 518
Dave New Avatar asked May 29 '13 08:05

Dave New


2 Answers

Having a generic attribute is not possible in a conventional way. However C# and VB don't support it but the CLR does. If you want to write some IL code it's possible.

Let's take your code:

[AttributeUsage(AttributeTargets.Property)] public class RelatedPropertyAttribute: Attribute {     public string RelatedProperty { get; private set; }      public RelatedPropertyAttribute(string relatedProperty)     {        RelatedProperty = relatedProperty;     } } 

Compile the code, open up the assembly with ILSpy or ILDasm and then dump the content to a text file. The IL of you attribute class declaration will look like this:

.class public auto ansi beforefieldinit RelatedPropertyAttribute extends [mscorlib]System.Attribute 

In the text file, you can then make the attribute generic. There are several things that need to be changed.

This can simply be done by changing the IL and the CLR won't complain:

.class public abstract auto ansi beforefieldinit       RelatedPropertyAttribute`1<class T>       extends [mscorlib]System.Attribute 

and now you can change the type of relatedProperty from string to your generic type.

For Example:

.method public hidebysig specialname rtspecialname      instance void .ctor (         string relatedProperty     ) cil managed 

change it to:

.method public hidebysig specialname rtspecialname      instance void .ctor (         !T relatedProperty     ) cil managed 

There are lot of frameworks to do a "dirty" job like that: Mono.Cecil or CCI.

As I have already said it's not a clean object oriented solution but just wanted to point out another way to break the limit of C# and VB.

There's an interesting reading around this topic, check it out this book.

Hope it helps.

like image 194
codingadventures Avatar answered Sep 21 '22 07:09

codingadventures


You cannot

  • you cannot create generic attribute types (it simply isn't allowed); equally, no syntax for using generic attributes ([Foo<SomeType>]) is defined
  • you cannot use lambdas in attribute initializers - the values available to pass to attributes is very limited, and simply does not include expressions (which are very complex, and are runtime objects, not compile-time literals)
like image 39
Marc Gravell Avatar answered Sep 24 '22 07:09

Marc Gravell