tags:

views:

1134

answers:

5

My particular problem:

I have a string which specifies an aribitrary type in a configuration class

Config.numberType = "System.Foo";

where Foo is a type like Decimal or Double

I use Type.GetType(Config.numberType) to return the corresponding type.

How do I get from that type to being able to use, System.Foo.TryParse() ?

Some further related queries

  • TryParse() can be accessed from System.Foo.TryParse() as well as foo.TryParse(). Does this mean foo is some kind of class in C#? This seems weird to me that int, double etc are actually not just modifier keywords.
  • How can you declare variables under these circumstances? - var is not universally usable it seems i.e. only in local scope etc.
+7  A: 

How do I get from that type to being able to use, System.Foo.TryParse() ?

You'll need to use reflection to look up and then invoke the static TryParse() method. Not all types implement this method - so you'll have to decide how to handle it if it's missing. You could also use System.Convert to convert a string to an arbitrary type, assuming the string is actually a valid representation of a value for that type and there's a conversion implemented for it.

TryParse() can be accessed from System.Foo.TryParse() as well as foo.TryParse(). Does this mean foo is some kind of class in C#?

int, double, etc. are aliases for System.Int32, System.Double, etc. - they're part of the C# language, which would be uncomfortably verbose without them.

How can you declare variables under these circumstances?

Without knowing the type at compile-time, you'll be forced to declare and work with your data as object / System.Object. C# 4.0 will introduce actual dynamic types that will take care of some of the tedious reflection work for you, but for now you're stuck doing it by hand. Note that if you use System.Convert in a method with a parametrized type argument and return, or TryParse() using a technique such as that linked to by Sebastian Sedlak, you can easily achieve the ability to write client code that works with static types... So long as they match or can be converted to from the types you're parsing.

Shog9
Except TryParse is static; `dynamic` applies to instances.
Marc Gravell
@Marc: I meant that as an answer to his last question (what type can you use for variables when you don't know the type at compile-time). But yeah, `dynamic` will do little to help with his first problem. The code Sebastian linked to looks like a decent way to accomplish that.
Shog9
+11  A: 

As many have said - there isn't a direct route. I expect one close option is TypeConverter:

    Type type = typeof(double);
    string text = "123.45";

    object value = TypeDescriptor.GetConverter(type)
        .ConvertFromInvariantString(text);

Of course, you may need try/catch to handle exceptions. Such is life.

Marc Gravell
Note that unlike most options, this is extensible and self-maintaining; many standard and bespoke types have associated converters.
Marc Gravell
+4  A: 

EDITED: I removed the generic implementation and cleaned up this response to fit better to the originally stated problem.

NOTE: The answer by Marc Gravell is probably the most concise if you just want the parsed value given the type. The answer below shows you how to get at the method (i.e., the MethodInfo object and how to invoke it).

The following should work, at least for types that implement public static bool TryParse(string, T value):

public static class Parsing
{
    static MethodInfo findTryParseMethod(Type type)
    {
        //find member of type with signature 'static public bool TryParse(string, out T)'
        BindingFlags access = BindingFlags.Static | BindingFlags.Public;
        MemberInfo[] candidates = type.FindMembers(
            MemberTypes.Method,
            access,
            delegate(MemberInfo m, object o_ignored)
            {
                MethodInfo method = (MethodInfo)m;
                if (method.Name != "TryParse") return false;
                if (method.ReturnParameter.ParameterType != typeof(bool)) return false;
                ParameterInfo[] parms = method.GetParameters();
                if (parms.Length != 2) return false;
                if (parms[0].ParameterType != typeof(string)) return false;
                if (parms[1].ParameterType != type.MakeByRefType()) return false;
                if (!parms[1].IsOut) return false;

                return true;

            }, null);

        if (candidates.Length > 1)
        {
            //change this to your favorite exception or use an assertion
            throw new System.Exception(String.Format(
                "Found more than one method with signature 'public static bool TryParse(string, out {0})' in type {0}.",
                type));
        }
        if (candidates.Length == 0)
        {
            //This type does not contain a TryParse method - replace this by your error handling of choice
            throw new System.Exception(String.Format(
                "Found no method with signature 'public static bool TryParse(string, out {0})' in type {0}.",
                type));
        }
        return (MethodInfo)candidates[0];
    }

    public static bool TryParse(Type t, string s, out object val)
    {
        MethodInfo method = findTryParseMethod(t); //can also cache 'method' in a Dictionary<Type, MethodInfo> if desired
        object[] oArgs = new object[] { s, null };
        bool bRes = (bool)method.Invoke(null, oArgs);
        val = oArgs[1];
        return bRes;
    }

    //if you want to use TryParse in a generic syntax:
    public static bool TryParseGeneric<T>(string s, out T val)
    {
        object oVal;
        bool bRes = TryParse(typeof(T), s, out oVal);
        val = (T)oVal;
        return bRes;
    }
}

Use the following test code:

        public bool test()
    {
        try
        {
            object oVal;
            bool b = Parsing.TryParse(typeof(int), "123", out oVal);
            if (!b) return false;
            int x = (int)oVal;
            if (x!= 123) return false;
        }
        catch (System.Exception)
        {
            return false;
        }

        try
        {
            int x;
            bool b = Parsing.TryParseGeneric<int>("123", out x);
            if (!b) return false;
            if (x != 123) return false;
        }
        catch (System.Exception)
        {
            return false;
        }


        try
        {
            object oVal;
            bool b = Parsing.TryParse(typeof(string), "123", out oVal);
            //should throw an exception (//no method String.TryParse(string s, out string val)
            return false;
        }
        catch (System.Exception)
        {
            //should throw an exception
        }

        return true;
    }
}

And use this in your case:

    //input: string s, Config
Type tNum = Type.GetType(Config.numberType);    
object oVal;
bool ok = Parsing.TryParse(tNum, s, out oVal);
//oVal is now of type tNum and its value is properly defined if ok == true

About the use of var: you may have a misconception of what var does: It is not a "variant" type (the type object already is used for that) but moves the declaration syntax for the type to the assignment's right side. The following declarations are equivalent:

var i = 1;  //the compiler infers the type from the assignment, type of i is int.
int i = 1;  //type of i is int via declaration

The primary use of var is allowing to create anonymous types:

var anon = new { Name = "abc", X = 123 };
ILoveFortran
A: 

And another good link to this problem. The source code on that site is very bad formatted, so i putted it here. Hopefully, the author doesn't sue me.

public static T Parse<T>(string s)
{
    Type t = typeof(T);
    // Attempt to execute the Parse method on the type if it exists. 
    MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) });

    if (parse != null)
    {
        try
        {
            return (T)parse.Invoke(null, new object[] { s });
        }
        catch (Exception ex)
        {
            throw ex.InnerException;
        }
    }
    else
    {
        throw new MethodAccessException(String.Format("The Parse method does not exist for type {0}.", t.Name));
    }
}

public static bool TryParse<T>(string s, out T result)
{
    return TryParse<T>(s, false, out result);
}

public static bool TryParse<T>(string s, bool throwException, out T result)
{
    result = default(T);
    Type t = typeof(T);
    T type = default(T);

    // Look for the TryParse method on the type. 
    MethodInfo tryParse = t.GetMethod("TryParse", new Type[] { typeof(string), Type.GetType(t.FullName + "&") });
    if (tryParse != null)
    {
        // Try parse exists. Call it. 
        Object[] ps = new Object[2];
        ps[0] = s;

        bool isSuccess = (bool)tryParse.Invoke(type, ps);

        if (isSuccess)
            result = (T)ps[1];

        return isSuccess;
    }
    else
    {
        // TryParse does not exist. Look for a Parse method. 
        try
        {
            result = Parse<T>(s);
            return true;
        }
        catch
        {
            if (throwException)
                throw;

            return false;
        }
    }
}
Oliver