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.var
is not universally usable it seems i.e. only in local scope etc.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.
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.
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 };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With