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问题,但这就是重点-我知道有更简单/更好/更快的方法来实现相同的最终结果,但我对这种情况下的结果不感兴趣,但实现它的方法。