有没有办法在c#谓词中进行这种转换

时间:2011-11-18 21:15:22

标签: c# generics linq-to-xml

我正在尝试创建一个泛型方法,该方法将返回一个谓词来查找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,另一个转换为十进制。我真的不希望有这种重复。

6 个答案:

答案 0 :(得分:1)

任意类型T都没有implicit or explicit conversions。允许从XAttribute到另一种类型的唯一转换是明确的,并且适用于以下类型:

您必须创建采用上述类型之一的重载,并将调用限制为其中一种类型。

答案 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)

对通用方法的XAttribute约束:

 GetPredicate<T>(Criterion criterion) where T : XAttribute