C# 构建、迭代和调用不同类型的表达式列表

C# 构建、迭代和调用不同类型的表达式列表,c#,dictionary,expression-trees,C#,Dictionary,Expression Trees,考虑以下定义: public class AlternateDescriptionAttribute : Attribute { public string AlternateDescription { get; } public AlternateDescriptionAttribute(string s) { AlternateDescription = s; } } enum Metasyntactic { [AlternateD

考虑以下定义:

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()。换言之,使用替代的描述:它就在那里,如果不存在则返回到描述,如果两者都不存在则返回到字符串

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

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;
}
公共静态bool TryGetAttributeValue(枚举字段、表达式值表达式、输出T值)
其中:属性
{
var属性=TryGetAttribute(字段);
if(属性==null)
{
值=默认值(T);
返回false;
}
value=valueExpression.Compile()(属性);
返回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();
}
静态字符串描述(枚举字段)
{
if(TryGetAttributeValue(字段,a=>a.alternatedDescription,out字符串alternateDesc))
{
返回备用数据集;
}
if(TryGetAttributeValue(字段,a=>a.说明,输出字符串说明))
{
返回说明;
}
返回字段.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();
}
静态字符串GetNiceScriptionViaExpressions(枚举字段)
{
表达式exp1=a=>a.alternativeDescription;
表达式exp2=a=>a.描述;
var expressions=newlambdaexpression[]{exp1,exp2,};
foreach(表达式中的var exp)
{
var attributeType=exp.Parameters[0]。类型;
var attributeInstance=field.GetType().GetField(field.ToString()).GetCustomAttributes(attributeType,false).FirstOrDefault();
if(attributeInstance==null)
{
继续;
}
var result=exp.Compile().DynamicInvoke(attributeInstance);
如果(结果!=null)
{
返回(字符串)结果;
}
}
返回字段.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();
}
静态字符串GetNiceScriptionViaExpressions(枚举字段)
{
//AttributeExpressionDictionary将是一个字典映射
//表达式的属性类型-不确定其外观。。。
foreach(AttributeExpressionDictionary中的var属性)
{
if(TryGetAttributeValue(字段、属性值、输出描述))
{
返回说明;
}
}
返回字段.ToString();
}

这可能吗?如果不是,可以对<代码> GETNICEDATIONTIVIVAY表达式< /代码>进行什么改进以使其更安全和/或性能?

< P>您是否考虑过使用“代码>权重> <代码>属性的单个属性定义,并对<代码> 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; }
}
“重量”属性将用作要使用的说明

以下是如何使用注释枚举:

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

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

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

GetDescription方法的实现:

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

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

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

我觉得你把这件事搞得太复杂了。我看不出表达式树在这里有什么用处。如果我理解正确的话,你的主要问题是泛型,而不是表达式树。您希望拥有一个具有不同泛型参数的对象集合,并且仍然能够以类型安全的方式访问它们。据我所知,没有办法做到这一点。例如,您将始终必须通过对象/基类到达某个位置,如:

public class TestDescription : Attribute
{
    public string Desc {get; set;}
}

public interface ITextExtractor
{
    string GetText(Attribute attribute);
    Attribute GetAttribute(Enum field);
}

public class TextExtractor<TAttribute> : ITextExtractor
    where TAttribute: Attribute
{
    public Func<TAttribute, string> TextGetter {get; private set;}
    public TextExtractor(Func<TAttribute, string> getter){ TextGetter = getter;}
    public Attribute GetAttribute(Enum field)
    {
        return ...;
    }
    public string GetText(Attribute attribute) { return TextGetter((TAttribute)attribute);}
}
公共类TestDescription:属性
{
公共字符串Desc{get;set;}
}
公共接口iTextractor
{
字符串GetText(属性);
属性GetAttribute(枚举字段);
}
公共类文本提取器:ITextExtractor
其中:属性
{
public Func TextGetter{get;private set;}
公共文本提取器(Func getter){TextGetter=getter;}
公共属性GetAttribute(枚举字段)
{
返回。。。;
}
公共字符串GetText(属性){return TextGetter((tatAttribute)属性);}
}
然后

    var possibleAttributes = new List<ITextExtractor>{
        new TextExtractor<TestDescription>(a => a.Desc),
        new TextExtractor<DescriptionAttribute>(a => a.Description)};

    foreach (var possibleAttribute in possibleAttributes)
    {
        var attribute = possibleAttribute.GetAttribute(field);
        if (attribute != null) return possibleAttribute.GetText(attribute);

    }
var-possibleAttributes=新列表{
新的文本提取器(a=>a.Desc),
新的文本提取器(a=>a.Description)};
foreach(可能属性中的可能属性变量)
{
var attribute=possibleAttribute.GetAttribute(字段);
if(attribute!=null)返回possibleAttribute.GetText(attribute);
}

您还可以回到属性基类中的属性基类,然后必须转换为正确的类型。

谢谢-我知道这个问题被认为是一个XY问题,但这就是重点-我知道有更简单/更好/更快的方法来实现相同的最终结果,但我对这种情况下的结果不感兴趣,但实现它的方法。