是否有一种方法可以在c#谓词中实现这种类型的强制转换?
本文关键字:种类 实现 类型 转换 谓词 一种 方法 是否 | 更新日期: 2023-09-27 18:15:04
我正在尝试创建一个通用方法,该方法将返回谓词以查找XML文档中的元素。
基本上是这样的:
private static Func<XElement, bool> GetPredicate<T>(Criterion criterion)
{
switch (criterion.CriteriaOperator)
{
case CriteriaOperator.Equal:
return x => (T)x.Attribute(criterion.PropertyName) ==
(T)(criterion.PropertyValue);
case CriteriaOperator.GreaterThan:
return x => (T)x.Attribute(criterion.PropertyName) >
(T)(criterion.PropertyValue);
case CriteriaOperator.GreaterThanOrEqual:
return x => (T)x.Attribute(criterion.PropertyName) >=
(T)(criterion.PropertyValue);
case CriteriaOperator.LessThan:
return x => (T)x.Attribute(criterion.PropertyName) <
(T)(criterion.PropertyValue);
case CriteriaOperator.LessThanOrEqual:
return x => (T)x.Attribute(criterion.PropertyName) <=
(T)(criterion.PropertyValue);
case CriteriaOperator.NotEqual:
return x => (T)x.Attribute(criterion.PropertyName) !=
(T)(criterion.PropertyValue);
default:
throw new ArgumentException("Criteria Operator not supported.");
}
}
唯一的问题是这不能编译。问题是在(T)x.Attribute(criterion.PropertyName)
部分,其中编译器指示:
不能强制转换'System.Xml.Linq '类型的表达式。XAttribute'为类型"T"
目前我有两个相同的方法,除了一个强制转换为double,另一个强制转换为decimal。我真的不希望有这样的重复。
XAttribute class定义了几个转换操作符。但是,当转换为泛型类型参数T
时,不考虑这些操作符。
你可以做的是在运行时构造lambda表达式,如下所示:
private static Func<XElement, bool> GetPredicate<T>(Criterion criterion)
{
var arg = Expression.Parameter(typeof(XElement), "arg");
var name = Expression.Constant((XName)criterion.PropertyName);
var attr = Expression.Call(arg, "Attribute", null, name);
var left = Expression.Convert(attr, typeof(T));
var right = Expression.Constant(criterion.PropertyValue, typeof(T));
Expression body;
switch (criterion.CriteriaOperator)
{
case CriteriaOperator.Equal:
body = Expression.Equal(left, right);
break;
case CriteriaOperator.GreaterThan:
body = Expression.GreaterThan(left, right);
break;
default:
throw new ArgumentException("Criteria Operator not supported.");
}
return Expression.Lambda<Func<XElement, bool>>(body, arg).Compile();
}
用法:
var f = GetPredicate<int>(new Criterion("documentversion", CO.GreaterThan, 8));
var g = GetPredicate<string>(new Criterion("documentid", CO.Equal, "DOC-5X"));
var h = GetPredicate<double>(new Criterion("documentprice", CO.Equal, 85.99d));
没有到任意类型T
的隐式或显式转换。唯一允许的从XAttribute
到其他类型的转换是显式的,并且是以下类型:
-
Boolean
-
Nullable<Boolean>
-
DateTime
-
Nullable<DateTime>
-
DateTimeOffset
-
Nullable<DateTimeOffset>
-
Decimal
-
Nullable<Decimal>
-
Double
-
Nullable<Double>
-
Guid
-
Nullable<Guid>
-
Int32
-
Nullable<Int32>
-
Int64
-
Nullable<Int64>
-
Single
-
Nullable<Single>
-
String
-
TimeSpan
-
Nullable<TimeSpan>
-
UInt32
-
Nullable<UInt32>
-
UInt64
-
Nullable<UInt64>
您必须创建采用上述类型之一的重载,并将调用限制为其中之一。
如果您只是将对T
的强制转换替换为对dynamic
的强制转换,那么它将工作。我不会因为在这里抛弃类型安全而感到遗憾,因为您可能无法确保XML属性中的内容是正确的类型——所以类型安全一直是一种错觉。
T
应该是什么?除非对泛型有某种约束,否则XAttribute
不能转换为它。在任何情况下,您可能希望获得Attribute().Value
,这是一个字符串。然后你可以做一个比较。criterion.PropertyValue
是什么类型的?
如果XML包含像数字这样的原语,则不能直接将字符串强制转换为数字。您需要使用double.TryParse()
之类的方法。不幸的是,我知道没有办法约束一个泛型有一个TryParse
方法。如果有的话,你可以说T.TryParse
。但是没有办法,所以你不能。泛型可能不会帮到你。
你不能用==
来比较两个T
,但object.Equals()
应该可以。
Convert.ChangeType()
:
case CriteriaOperator.Equal:
return x => object.Equals(
Convert.ChangeType(x.Attribute(criterion.PropertyName).Value, typeof(T)),
criterion.PropertyValue);
这样做的问题是XML在某些情况下使用不同的转换规则(例如Double.PositiveInfinity
被表示为INF
)。
要解决这个问题,可以使用XmlConvert
类,它在内部由转换操作符使用。除了它没有像Convert.ChangeType()
这样的"通用"方法,所以你必须自己创建:
private static object Convert(string value, Type targetType)
{
if (targetType == typeof(double))
return XmlConvert.ToDouble(value);
…
throw new ArgumentException();
}
…
case CriteriaOperator.Equal:
return x => object.Equals(
Convert(x.Attribute(criterion.PropertyName).Value, typeof(T)),
criterion.PropertyValue);
为泛型方法添加XAttribute
约束:
GetPredicate<T>(Criterion criterion) where T : XAttribute