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;
:
:
}
}
精彩评论