3

My task is to write a method StringToType() that converts a string to the specified type T.

  1. For primitive types, I use the method Convert.ChangeType()
  2. For enum-types - Enum.TryParse()
  3. For all other custom types, I created an interface "IConvertibleFromString" that contains a method "FromString()" to convert the string to the specified type. Any class that will need to convert from string must implement this interface.

But me do not like the way I implemented method StringToType(). I would like to use less than the reflection and ensure as much as possible the performance.

Please advise how best to implement/change it.

class Program
{
    static bool StringToType<T>(string str, ref T value)
    {
        Type typeT = typeof(T);
        bool isSuccess = false;
        if (typeT.GetInterface("IConvertibleFromString") != null)
        {
            return (bool)typeT.GetMethod("FromString").Invoke(value, new object[] { str });
        }
        else if (typeT.IsEnum)
        {
            MethodInfo methodTryParse = typeT.GetMethod("TryParse").MakeGenericMethod(typeT);
            return (bool)methodTryParse.Invoke(null, new object[] { str, value });
        }
        else if (typeT.IsPrimitive)
        {
            value = (T)Convert.ChangeType(str, typeT);
            return true;
        }
        return isSuccess;
    }

    static void Main(string[] args)
    {
        string intStr = "23";
        int val1 = 0;
        bool res = StringToType<int>(intStr, ref val1);
        Class1 c1;
        res = StringToType<Class1>(intStr, ref c1);
        Console.ReadKey();
    }
}

interface IConvertibleFromString
{
    bool FromString(string str);
}

class MySomeClass : IConvertibleFromString
{
    int someVal;

    public bool FromString(string str)
    {
        return int.TryParse(str, out someVal);
    }
}
2
  • A simple optimisation is to check the boolean conditions in the cheapest order (assuming GetInterface() will be the least efficient) Commented May 20, 2013 at 16:11
  • 2
    instead of doing if (typeT.GetInterface("IConvertibleFromString") != null) { return (bool)typeT.GetMethod("FromString").Invoke(value, new object[] { str }); } you can do var iface = value as IConvertibleFromString; if (iface != null) return iface.FromString(str);. That will save you the reflection penalty Commented May 20, 2013 at 16:31

3 Answers 3

5

This seemed to perform the best for me. Threw a million iterations at it with various consumers. It's a combination of folks' comments, with a little extra.

    static Boolean TryParseString<T>(
        String stringValue, ref T value)
    {

        Type typeT = typeof(T);

        if (typeT.IsPrimitive)
        {
            value = (T)Convert.ChangeType(stringValue, typeT);
            return true;
        }
        else if (typeT.IsEnum)
        {
            value = (T)System.Enum.Parse(typeT, stringValue); // Yeah, we're making an assumption
            return true;
        }
        else
        {
            var convertible = value as IConvertible;
            if (convertible != null)
            {
                return convertible.FromString(stringValue);
            }
        }

        return false;

    }
Sign up to request clarification or add additional context in comments.

1 Comment

Huh? IConvertible.FromString()? Where did you get that from?
1

For case #1 it's already optimal.

For case #2 you could use Enum.Parse and catch ArgumentException and return false.

For case #3 method FromString is either a static factory method and therefore is not contained in an interface IConvertibleFromString (so the interface is just a type marker and contains no methods) or it is an instance method that mutates this._value or something, it's not clear. If it's the latter then just cast value to IConvertibleFromString and call FromString, no need for reflection. If it's a static factory method then you'd have to use reflection.

Comments

0

The current structure of things doesn't allow it to be a real StringToType because you can't call FromString befre you have an 'already active' instance. When your FromString is called, another instance is created and returned - This means that you'll need to creat 2 instances instances each time to achieve what you want.

If you want it to work perfectly, here's my suggestion:

  1. Create a new class - "StringActivator" (inspired by Reflection's Activator)

  2. Create a method of Type - StringActivator.CreateInstance(string...)

  3. The activation string is a string consisting of a comma delimited / xml string where each item is a constructor parameter.

  4. Inside, go over the types (T) constructors and find all constructors with the requested number of parameters (as sent in the activation string).

  5. now, once you have all constructors, try to convert each parameter value you sent in the activation string to the type which the constructor expects using Recursion (call StringActivator.CreateInstane where T is the type which the constructor expects).

For this to work, you'll need to check in the beginning of your CreateInstance if T is a Primitive or an ENum (Stop condition for the Recursion) and then use (T)Convert for that.

  1. When you're converting all your comma delimited parameters use Reflection.Emit to create a compiled call for your and specific number of parameters sent.

  2. Map the emitted functions' delegate to the Type sent () and call it the next time you try to convert to T from a string with the same number of parameters...

Hope it helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.