开发者

Linq-to-XML explicit casting in a generic method

I've looked for a similar question, but the only one that was close didn't help me in the end.

I have an XML file that looks like this:

<Fields>
    <Field name="abc" value="2011-01-01" />
    <Field name="xyz" value="" />
    <Field name="tuv" value="123.456" />
</Fields>

I'm trying to use Linq-to-XML to get the values from these fields. The values can be of type Decimal, DateTime, String and Int32. I was able to get the fields one by one using a relatively simple query. For example, I'm getting the 'value' from the field with the name 'abc' using the following:

private DateTime GetValueFromAttribute(IEnumerable<XElement> fields, String attName)
{
    return (from field in fields
           where field.Attribute("name").Value == "abc"
           select (DateTime)field.Attribute("value")).FirstOrDefault()
}

this is placed in a separate function that simply returns this value, and everything works fine (since I know that there is only one element with the name attribute set to开发者_StackOverflow中文版 'abc'). however, since I have to do this for decimals and integers and dates, I was wondering if I can make a generic function that works in all cases. this is where I got stuck. here's what I have so far:

private T GetValueFromAttribute<T>(IEnumerable<XElement> fields, String attName)
{
    return (from field in fields
            where field.Attribute("name").Value == attName
            select (T)field.Attribute("value").Value).FirstOrDefault();
}

this doesn't compile because it doesn't know how to convert from String to T. I tried boxing and unboxing (i.e. select (T) (Object) field.Attribute("value").Value but that throws a runtime Specified cast is not valid exception as it's trying to convert the String to a DateTime, for instance.

Is this possible in a generic function? can I put a constraint on the generic function to make it work? or do I have to have separate functions to take advantage of Linq-to-XML's explicit cast operators?


You can try do it this way:

public static T GetValueFromAttribute<T>(IEnumerable<XElement> fields, String attName)
{
  return (from field in fields
          where field.Attribute("name").Value == attName
          select (T)Convert.ChangeType(field.Attribute("value").Value, typeof(T))).FirstOrDefault();
}

It will work for DateTime, int etc (all types that implement IConvertible).


You can't use operator overloading like that, no. The closest you'd come would be to have something like:

private T GetValueFromAttribute<T>(IEnumerable<XElement> fields, 
                                   string attName,
                                   Func<XAttribute, T> selector)
{
    return fields.Where(field => field.Attribute("name").Value == attName)
                 .Select(field => selector(field.Attribute("value")))
                 .FirstOrDefault();
}

then call it with:

GetValueFromAttribute(fields, "foo", attr => (DateTime) attr);


No, a cast to a generic variable T always performs a cast; no attempt is made to invoke an explicit or implicit conversion operator instead.


I'd suggest to give up the idea of calling a method for each element whose value you want to retrieve, and to iterate the enumerable of elements instead:

foreach (var field in fields.Elements("field"))
{
    var name = field.Attribute("name");
    var value = field.Attribute("value");

    switch ((string)name)
    {
    case "abc":
        var result = (DateTime)value;
        :
        :
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜