Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast object to T

I'm parsing an XML file with the XmlReader class in .NET and I thought it would be smart to write a generic parse function to read different attributes generically. I came up with the following function:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

As I came to realise, this does not work entirely as I have planned; it throws an error with primitive types such as int or double, since a cast cannot convert from a string to a numeric type. Is there any way for my function to prevail in modified form?

like image 522
Kasper Holdum Avatar asked May 22 '09 19:05

Kasper Holdum


People also ask

How do you cast an object to a specific class?

Class class is used to cast the specified object to the object of this class. The method returns the object after casting in the form of an object. Return Value: This method returns the specified object after casting in the form of an object.

How do I cast one object to another in C#?

C# includes another method of performing explicit conversions. Using the "as" operator, an object can be converted from one type to another. Unlike with explicit casting, if the conversion is not possible because the types are incompatible the operation does not throw an exception.


8 Answers

First check to see if it can be cast.

if (readData is T) {
    return (T)readData;
} 
try {
   return (T)Convert.ChangeType(readData, typeof(T));
} 
catch (InvalidCastException) {
   return default(T);
}
like image 106
Bob Avatar answered Oct 29 '22 05:10

Bob


Have you tried Convert.ChangeType?

If the method always returns a string, which I find odd, but that's besides the point, then perhaps this changed code would do what you want:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)Convert.ChangeType(readData, typeof(T));
}
like image 41
Lasse V. Karlsen Avatar answered Oct 29 '22 04:10

Lasse V. Karlsen


try

if (readData is T)
    return (T)(object)readData;
like image 27
Sadegh Avatar answered Oct 29 '22 05:10

Sadegh


You could require the type to be a reference type :

 private static T ReadData<T>(XmlReader reader, string value) where T : class
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     return (T)readData;
 }

And then do another that uses value types and TryParse...

 private static T ReadDataV<T>(XmlReader reader, string value) where T : struct
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     int outInt;
     if(int.TryParse(readData, out outInt))
        return outInt
     //...
 }
like image 24
Tom Ritter Avatar answered Oct 29 '22 03:10

Tom Ritter


Actually, the problem here is the use of ReadContentAsObject. Unfortunately, this method does not live up to its expectations; while it should detect the most appropirate type for the value, it actually returns a string, no matter what(this can be verified using Reflector).

However, in your specific case, you already know the type you want to cast to, therefore i would say you are using the wrong method.

Try using ReadContentAs instead, it's exactly what you need.

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAs(typeof(T), null);
    return (T)readData;
}
like image 27
baretta Avatar answered Oct 29 '22 03:10

baretta


Add a 'class' constraint (or more detailed, like a base class or interface of your exepected T objects):

private static T ReadData<T>(XmlReader reader, string value) where T : class
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

or where T : IMyInterface or where T : new(), etc

like image 36
Ricardo Villamil Avatar answered Oct 29 '22 05:10

Ricardo Villamil


You can presumably pass-in, as a parameter, a delegate which will convert from string to T.

like image 34
ChrisW Avatar answered Oct 29 '22 03:10

ChrisW


Actually, the responses bring up an interesting question, which is what you want your function to do in the case of error.

Maybe it would make more sense to construct it in the form of a TryParse method that attempts to read into T, but returns false if it can't be done?

    private static bool ReadData<T>(XmlReader reader, string value, out T data)
    {
        bool result = false;
        try
        {
            reader.MoveToAttribute(value);
            object readData = reader.ReadContentAsObject();
            data = readData as T;
            if (data == null)
            {
                // see if we can convert to the requested type
                data = (T)Convert.ChangeType(readData, typeof(T));
            }
            result = (data != null);
        }
        catch (InvalidCastException) { }
        catch (Exception ex)
        {
            // add in any other exception handling here, invalid xml or whatnot
        }
        // make sure data is set to a default value
        data = (result) ? data : default(T);
        return result;
    }

edit: now that I think about it, do I really need to do the convert.changetype test? doesn't the as line already try to do that? I'm not sure that doing that additional changetype call actually accomplishes anything. Actually, it might just increase the processing overhead by generating exception. If anyone knows of a difference that makes it worth doing, please post!

like image 34
genki Avatar answered Oct 29 '22 03:10

genki