2014-09-29 4 views
1

У меня есть перечисление с пользовательским атрибутом маркировки пунктов:Изучения перечисления атрибутов из дерева выражения

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

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

Затем я создаю дерево выражения, которое использует перечисление:

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

Я тогда синтаксический анализ дерева выражений и перевести его в строковое представление выражения. Результирующая строка, что я хочу это:

"environment.colour == '#ff0000'" 

У меня есть проблема в том, что значение перечисления получает превратился в константу в лямбда, поэтому при взгляде на дерево выражения, он видит постоянное значение 0 вместо выражение, читающее красный элемент перечисления.

Я хочу использовать настраиваемый атрибут, чтобы идентифицировать перечисление как особый случай, и заменить его на значение, прикрепленное к атрибуту, но я не могу, потому что все, что я вижу, - это значение константы 0.

Как я могу получить перечисление, используемое для создания константы в дереве выражений?

Если вы не можете, то как же я могу сделать что-то подобное?

+0

BTW, конвенция должна вызвать параметр лямбда '_', если вы * не * собираетесь использовать его. – svick

+0

@svick: это документировано где угодно? – BG100

+0

Не задокументировано, так как я считаю, что это не официальное соглашение, но вы можете посмотреть, например, http://stackoverflow.com/q/2778362/41071. – svick

ответ

2

Для конкретного примера работает следующий код.

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

На данный момент, я жёстко == оператора, если вы хотите, чтобы сделать его динамичным вам нужно осмотреть binaryExpression.NodeType недвижимость.

Примечание: Это не будет работать должным образом, если ваше перечисление имеет повторяющиеся значения. (I.e) Более одного поля перечисления с одинаковым значением. Не проблема с вышеуказанным кодом, Enum.ToObject не работает, когда находит дубликат. На самом деле большинство методов перечисления не будут работать должным образом, если у вас есть дубликаты.

+0

Это будет работать только для одного примера, это не гарантирует, что любое произвольное выражение будет использовать этот атрибут для рендеринга этого перечисления при преобразовании его в строку – Servy

+0

@Servy Я уже сказал это в своем ответе. Кроме того, ОП не просил об общем решении (если я что-то не замечаю). –

+0

Что заставляет вас думать, что у него никогда не будет каких-либо других выражений, которые он будет использовать?Он спросил, как определить, как это перечисление выводится из деревьев выражений. Это не так. – Servy

1

Тип Enum не для этого! Неправильно использовать пользовательские атрибуты пользователя с Enum, это способ отвлечения внимания. Наконец, это всегда становится монстром, который трудно поддерживать. Вы лучше создать некоторый абстрактный класс:

public abstract class DescriptedCodeValue 
{ 
    protected DescriptedCodeValue(int id, string description) 
    { 
     Id = id; 
     Description = description; 
    } 
    public int Id { get; private set; } 
    public string Description { get; private set; } 

    public static implicit operator int(DescriptedCodeValue val) 
    { 
     return val.Id; 
    } 

    public static implicit operator string(DescriptedCodeValue val) 
    { 
     return val.Description; 
    } 

    public override string ToString() 
    { 
     return Description; 
    } 
} 

После этого вы просто унаследовать его, к примеру, как это:

public class ColorCode : DescriptedCodeValue 
{ 
    private ColorCode(int id, string description) : base(id, description) { } 
    public static ColorCode Red = new ColorCode(1, "#ff0000"); 
    public static ColorCode Green = new ColorCode(2, "#00ff00"); 
} 

Финлей, это просто пример. Вы можете легко оптимизировать этот класс для своих нужд, и основная прибыль заключается в том, что вы можете легко расширить функциональность везде, где хотите, без серьезных изменений в проекте.

+0

Я бы сказал, что это тоже правильный ответ, и он будет работать так же хорошо ... но я решил реализовать ответ ШрирамСактивеля, поэтому поэтому я пометил его ответ как правильный. Если бы я мог выбрать два ответа, я бы сказал: – BG100

Смежные вопросы