【问题标题】:Examining enum attributes from an expression tree检查表达式树中的枚举属性
【发布时间】:2014-09-29 13:15:15
【问题描述】:

我有一个带有标记项目的自定义属性的枚举:

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

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

然后我创建一个使用枚举的表达式树:

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

然后我解析表达式树并将其转换为表达式的字符串表示形式。我想要的结果字符串是:

"environment.colour == '#ff0000'"

我遇到的问题是枚举值在 lambda 中变成了一个常量,因此在查看表达式树时,它会看到常量值 0 而不是读取枚举的红色项的表达式。

我想使用自定义属性将枚举标识为特殊情况,并将其值替换为附加到属性的值,但我不能,因为我只能看到常量0 值。

如何获取用于在表达式树中创建常量的枚举?

如果你不能,那我还能怎么做类似的事情?

【问题讨论】:

  • 顺便说一句,约定是调用 lambda 参数 _ 如果您要使用它。
  • @svick:这在任何地方都有记录吗?
  • 没有真正记录,因为我相信这不是官方约定,但你可以看看例如stackoverflow.com/q/2778362/41071

标签: c# enums lambda expression-trees


【解决方案1】:

对于特定示例,以下代码有效。

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 属性。

注意:当您的枚举具有重复值时,这将无法正常工作。 (即)多个具有相同值的枚举字段。上面的代码不是问题,Enum.ToObject 在找到重复项时会损坏。事实上,当您有重复时,枚举中的大多数方法都无法正常工作。

【讨论】:

  • 这仅适用于该示例情况,它不能确保任何任意表达式在将其转换为字符串时都会使用此属性来呈现该枚举
  • @Servy 我已经在回答中说过了。 OP也没有要求通用解决方案(除非我忽略了某些东西)。
  • 是什么让您认为他永远不会使用任何其他类型的表达方式?他询问如何确定这个枚举是如何从表达式树中呈现的。这不会那样做。
  • @SriramSakthivel:好的,是的,我明白了……我知道这些限制。我的问题只是专门询问如何从表达式中访问附加到枚举的自定义属性,您回答得很好:) 我完全知道它检查运算符 LHS 上的类型以及表达式仅限于此。我很生气你的回答被否决了!
  • @Servy:如果我没记错的话,微软做了一些与我对 Linq to SQL 所做的非常相似的事情:他们采用表达式树,然后遍历树将表达式转换为SQL 表达式。 SQL 并不支持所有可能的表达式,当您收到 does not recognize the xxx method, and this method cannot be translated into a store expression 异常时就很明显了。我正在做的没有什么不同。你是说微软也做错了吗?唯一的区别是我将表达式转换为 EcmaScript 而不是 SQL。
【解决方案2】:

枚举类型不是为了那个!在 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");
}

最后,这只是一个例子。您可以轻松地优化该类以满足您的需求,主要的好处是您可以轻松地在任何地方扩展功能,而无需对项目进行重大更改。

【讨论】:

  • 我会说这也是一个正确的答案,并且同样有效......但我选择实施 SriramSakthivel 的答案,所以我将他的答案标记为正确。如果我可以选择两个答案,那么我会:)
猜你喜欢
  • 2015-09-12
  • 2012-04-21
  • 2019-04-22
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多