我正在尝试创建一个泛型方法,该方法将返回一个谓词来查找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,另一个转换为十进制。我真的不希望有这种重复。
答案 0 :(得分:1)
任意类型T
都没有implicit or explicit conversions。允许从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>
您必须创建采用上述类型之一的重载,并将调用限制为其中一种类型。
答案 1 :(得分:1)
XAttribute Class定义了多个conversion operators。但是,在转换为泛型类型参数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));
答案 2 :(得分:1)
如果你只是使用强制转换为T
将你的演员阵容替换为dynamic
,那么它就可以了。因为你可能无法确保XML属性中的东西是正确的类型,所以我不会在这里丢掉类型安全感觉不好 - 所以类型安全一直是一种错觉。
答案 3 :(得分:0)
T
应该是什么?除非对泛型有某种约束,否则无法将XAttribute
转换为它。在任何情况下,您可能希望得到Attribute().Value
,这是一个字符串。然后你可以做一个比较。 criterion.PropertyValue
的类型是什么?
如果您的XML包含数字等基元,则不能直接将字符串转换为数字。您需要使用double.TryParse()
之类的方法。不幸的是,我无法通过限制泛型来获得TryParse
方法。如果有的话,你可以说T.TryParse
。但是没有办法,所以你不能。泛型可能无法帮助你。
答案 4 :(得分:0)
您无法使用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
)。
要解决此问题,您可以使用转换运算符内部使用的the XmlConvert
class。除了它没有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);
答案 5 :(得分:-1)
GetPredicate<T>(Criterion criterion) where T : XAttribute