'Given a Type and a String, can you try to convert the String to that Type without interogating the Type?

I have a List<Type> that represent a signature for a text based command from a custom scripting language. I am trying to determine how to parse a String[] to a List<object> where the object types correlate to the List<Type>. Basically given a Type t I would like to try to cast a String s to that Type.

So far I have this

Type t = ...;
String s = ...;
if(Convert.ChangeType(s, t) != null)
{

}

Which I doubt is correct after thinking about Convert.ChangeType more. It is meant to simply change the type of the given object, not "parse" it. Seeing as you can't simply do (double)"ImAString" I would assume this method would be used to convert between object types that are "directly" castable.

Is there any general solution to this? Or is the only way to do this to create a large switch on the given Type and parse within each case(Most likely missing Types in the switch in the future)?

EDIT: Further, the return type of ChangeType is object. I assume that if I were to do a retObjectFromChange is double would be true (assuming I changed the type to a double)?



Solution 1:[1]

Convert.ChangeType() will parse strings to numbers when possible, throwing an exception on strings that aren't valid numbers.

Note: If you are going to convert between your own custom types a lot, you may want to use a TypeConverter instead. (TypeConverter will work better when converting both to-from primitive types like string.)

See: Convert System.String generically to any complex type using "Convert.ChangeType()"

EDIT: Yes, Convert.ChangeType("1.02", typeof(double)) is double should evaluate to true assuming no exceptions are thrown.

Solution 2:[2]

This is my current implementation. I found Guid, DateTime, enums, and nullables to be my edge cases. And I put in logic to handle empty strings and null values. I put in specific handling for some of the primitives because I found the TypeConverter to be slow in comparison for these, and they were my general case.

    public object GetValue(string readerValue, Type conversionType)
    {
        // if the type is a string, just return the value with no conversion
        if (conversionType == typeof(string) || conversionType == typeof(object))
        {
            return readerValue;
        }

        // if the field has a value try to cast it
        if (!string.IsNullOrWhiteSpace(readerValue))
        {
            readerValue = readerValue.Trim();

            if (conversionType.IsEnum)
            {
                return Enum.Parse(conversionType, readerValue);
            }
            else
            {
                Type underlyingType = Nullable.GetUnderlyingType(conversionType) ?? conversionType;

                if (underlyingType == typeof(int))
                {
                    return int.Parse(readerValue);
                }
                else if (underlyingType == typeof(bool))
                {
                    return bool.Parse(readerValue);
                }
                else if (underlyingType == typeof(DateTime))
                {
                    return DateTime.Parse(readerValue);
                }
                else if (underlyingType == typeof(double))
                {
                    return double.Parse(readerValue);
                }
                else if (underlyingType == typeof(long))
                {
                    return long.Parse(readerValue);
                }
                else if (underlyingType == typeof(Guid))
                {
                    return Guid.Parse(readerValue);
                }
                else
                {
                    // GetConverter and ConvertFrom are both slow, so only use it in a fallback
                    TypeConverter converter = TypeDescriptor.GetConverter(underlyingType);
                    return converter.ConvertFrom(readerValue);
                }
            }
        }
        // return null for nullable generic primitives
        else if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            return null;
        }
        // return the default value for non nullable primitive types
        else if (conversionType.IsValueType)
        {
            return Activator.CreateInstance(conversionType);
        }
        // return null for reference types
        else
        {
            return null;
        }
    }

Solution 3:[3]

one huge edge case is that this will explode if conversionType is null. right here:
if (conversionType.IsEnum)

Also the second check is problematic, Object could just be a box for null Object o = null; for example look at this code, it'll warn but compile fine. if O is null Type is now null. I could get into it a little more but there are other reasons I wouldn't just fire away in that second case for example

    Object o = null;
    Type T = o?.GetType() ?? null;

//Also signed vs. unsigned. there is also a ton of reflection in this solution. here is what i'd do, build a TypeMap by using and extending TypeCode, you could easily get 25 types without reflection

   if (conversionType == typeof(string) && !String.IsNullOrWhiteSpace(readerValue))
   {
     return conversionType;
   }

so now we know things, we don't want a string and we have null. Now we have to see if we are nested, if(typeof(conversiontype).isnested

  Type.GetTypeCode(conversionType);

in practice it's better to create a true Dictionary<int, String> so you can add your own types. Even better this returns 0 if conversion is null. you can also create a typemap dictionary on the fly and Cache it. It's got about twenty types and and as I said can be extended.

var TypeMap = Enum.GetValues(typeof(TypeCode)).Cast<TypeCode>().ToDictionary(t=>(int)t, f=>f.ToString());

after that simply get the type by calling gettype on the string. accepts all of those strings as arguments.

now here's the fun part, i've got about 30 or 40 types in my hashmap however if it's not in there
try
{
Type T = Type.GetType(CustomTypeMap[TypeCode])
if(TypeCode != 0)
// see if the type is nested and repeat
//the cases I had to add to TypeCode myself were GUID, ENUM, 
}
if you want to skip a step your dictionary can look like this as well

  Dictionary<int, Type> IntToType = new Dictionary<int, Type>()
    {
        [1] = typeof(bool),
        [2] = typeof(int),
        [3]  = typeof(double),
        [4]  = typeof(string),
    };

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3 jdmneon