请考虑以下定义:

public class AlternateDescriptionAttribute : Attribute
{
    public string AlternateDescription { get; }

    public AlternateDescriptionAttribute(string s)
    {
        AlternateDescription = s;
    }
}

enum Metasyntactic
{
    [AlternateDescription("Corge")]
    Foo,

    [AlternateDescription("Quux")]
    [Description("Qux")]
    Bar,

    Baz,
}


我想按优先顺序获取这些枚举的属性值,即AlternateDescription> Description> enum.ToString()。换句话说,使用AlternateDescription,如果不存在则返回到Description,如果都不存在则返回到ToString。

为此,我创建了以下辅助方法:

public static bool TryGetAttributeValue<TAttribute, T>(Enum field, Expression<Func<TAttribute, T>> valueExpression, out T value)
    where TAttribute : Attribute
{
    var attribute = TryGetAttribute<TAttribute>(field);
    if (attribute == null)
    {
        value = default(T);

        return false;
    }

    value = valueExpression.Compile()(attribute);

    return true;
}


使用方式如下:

static string GetNiceDescription(Enum field)
{
    if (TryGetAttributeValue<AlternateDescription, string>(field, a => a.AlternateDescription, out string alternateDesc))
    {
        return alternateDesc;
    }

    if (TryGetAttributeValue<DescriptionAttribute, string>(field, a => a.Description, out string description))
    {
        return description;
    }

    return field.ToString();
}


但是,这有点笨拙,尤其是因为我有两个以上我感兴趣的属性,并且将来可能还会有更多。我想做的是能够将属性及其关联的表达式放入列表中并对其进行迭代-到目前为止,我已经提出了以下内容:

static string GetNiceDescriptionViaExpressions(Enum field)
{
    Expression<Func<AlternateDescriptionAttribute, string>> exp1 = a => a.AlternateDescription;
    Expression<Func<DescriptionAttribute, string>> exp2 = a => a.Description;
    var expressions = new LambdaExpression[] { exp1, exp2, };

    foreach (var exp in expressions)
    {
        var attributeType = exp.Parameters[0].Type;
        var attributeInstance = field.GetType().GetField(field.ToString()).GetCustomAttributes(attributeType, false).FirstOrDefault();
        if (attributeInstance == null)
        {
            continue;
        }

        var result = exp.Compile().DynamicInvoke(attributeInstance);
        if (result != null)
        {
            return (string)result;
        }
    }

    return field.ToString();
}


但这是不雅观的,而且不是特别安全的编译时,如果有可能编写如下代码,我将更愿意:

static string GetNiceDescriptionViaExpressions(Enum field)
{
    // attributeExpressionsDictionary would be a dictionary mapping
    // attribute types to expressions - not sure how that would look...
    foreach (var attribute in attributeExpressionsDictionary)
    {
        if (TryGetAttributeValue<attribute.Key, string>(field, attribute.Value, out description))
        {
            return description;
        }
    }

    return field.ToString();
}


这可能吗?如果没有,可以对GetNiceDescriptionViaExpressions进行哪些改进以使其更安全和/或更高效?

最佳答案

您是否考虑过使用具有Weight属性的单个属性定义和enum字段的多个注释?

让单一属性负责您的描述要容易得多。

属性定义:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public sealed class DescriptionAttribute : Attribute
{
    public DescriptionAttribute(int weight, string value)
    {
        this.Weight = weight;
        this.Value = value;
    }

    public int Weight { get; }
    public String Value { get; }
}


Weight属性将用作要使用的描述的决胜局。

这是用于注释枚举的方法:

public enum SomeEnum
{
   [Description(1, "Official description"),
    Description(2, "Alternate Description")]
   Val1,

   [Description(1, "Description")]
   Val2,

   Val3


}

GetDescription方法的实现:

public static String GetDescription<TEnum>(TEnum @enum) where TEnum: struct
        {
            var description = typeof(TEnum)
                .GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
                .Single(x => EqualityComparer<TEnum>.Default.Equals((TEnum)x.GetValue(null), @enum))
                .GetCustomAttributes(typeof(DescriptionAttribute), inherit: false)
                .OfType<DescriptionAttribute>()
                .OrderBy(x => x.Weight)
                .Select(x => x.Value)
                .DefaultIfEmpty(@enum.ToString())
                .First();

            return description;
        }


注意:我使用的是通用版本,以避免将枚举值装箱。在当前版本中,该方法可以与任何结构一起使用,这是不可接受的。我的建议是使用运行时检查传递的值是否为实际枚举,以避免将来出现问题。

这是简单的用法:

// Official description
var fDescription = GetDescription(SomeEnum.Val1);

// Description
var sDescription = GetDescription(SomeEnum.Val2);

// Val3
var tDescription = GetDescription(SomeEnum.Val3);

关于c# - 构建,遍历和调用具有不同类型的表达式列表,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47199570/

10-11 05:42