Search code examples
c#enumslambdaexpression-trees

Examining enum attributes from an expression tree


I have an enum with a custom attribute marking the items:

enum MyColourEnum {        
    [RenderAs("'#ff0000'")]
    Red,

    [RenderAs("'#00ff00'")]
    Green
}

Then I create an expression tree that uses the enum:

Expression<Func<Environment,bool>> expr = _ => _.Colour == MyColourEnum.Red;

I then parse the expression tree and translate it into a string representation of the expression. The resulting string that I want is:

"environment.colour == '#ff0000'"

The problem I have is that the enum value gets turned into a constant within the lambda, so when looking at the expression tree, it sees the constant value 0 instead of an expression reading the Red item of the enum.

I want to use the custom attribute to identify the enum as a special case, and replace it's value with the one attached to the attribute, but I can't because all I can see is the constant 0 value.

How can I get the enum used to create the constant within the expression tree?

If you can't, then how else can I do something similar?


Solution

  • For the particular example, following code works.

    Expression<Func<Environment,bool>> expr = _ => _.Colour == MyColourEnum.Red;
    BinaryExpression binaryExpression = (BinaryExpression)expr.Body;
    
    var convert = (UnaryExpression)binaryExpression.Left;
    var propertyExpression = (MemberExpression)convert.Operand;
    var property = (PropertyInfo)propertyExpression.Member;
    
    Enum enumValue = (Enum)Enum.ToObject(property.PropertyType, ((ConstantExpression)binaryExpression.Right).Value); //
    FieldInfo fi = property.PropertyType.GetField(enumValue.ToString());
    var renderAs = fi.GetCustomAttribute<RenderAsAttribute>();
    
    if (renderAs != null)
    {
        String color = renderAs.Color;
    
        Console.WriteLine("{0}.{1} == {2}", property.DeclaringType.Name, property.Name, color);
    }
    

    For now, I hardcoded == operator, if you want to make it dynamic you need to inspect binaryExpression.NodeType property.

    Note: This won't work properly when your enum have duplicate values. (I.e) More than one enum field with same value. Not that problem with above code, Enum.ToObject is broken when it finds a duplicate. In fact most of the methods in enum won't work properly when you have duplicates.