0

I am trying to write a class to parse a XML config file with value strings to the corresponding values and type. An example entry of the config would be:

<ConfigItem Name="ParameterName" Value="1.3" Type="Double"/> 

I now would like to write a function to parse my value, so that I can do something like this:

double value = GetItemValue("//ConfigItem[@Name='ParameterName']"); 

Initially I tried overloading GetItemValue with its return type, but this is not possible in C#. I now tried using a generic function so that I would do:

double value = GetItemValue<double>("//ConfigItem[@Name='ParameterName']"); 

The function I tried to write is something like this:

public T GetItemValue<T>(string configName) { list = XmlConfig.SelectNodes(configName); T returnValue; if (T.TryParse(list[0].Attributes["Value"].Value, out returnValue)) return returnValue; else throw new InvalidProgramException("Could not parse: " + configName); } 

Unfortunately this does not work and I am getting the following error:

Error CS0119 'T' is a type parameter, which is not valid in the given context 

I am not sure what the problem is, but I suspect, it has to due with the fact, that it is not clear at compile time, whether all types T will have the method TryParse. I tried working around this by using a constraint like where T : Int, Double, but C# contraints do not support ValueTypes.

So what is the problem and how can I achieve, what I am trying to do? Please note for any suggestions that in the future I will also have to custom types for T.

1
  • If your're using int.TryParse() for your T.TryParse() method, you may have a bug. int.TryParse() parses numbers using the formatting information in a NumberFormatInfo object initialized for the current system culture. However, XML is conventionally culture-independent. I'd recommend using culture-independent methods, or methods from XmlConvert. Commented Feb 23, 2016 at 22:42

1 Answer 1

0

Standard types.

Assuming that you only need standard built-in primitive types you can get away with Convert or something along that line.

Proper way with custom types.

But if you need to support custom types, then you can use TypeConverter. It is a bulky way, but, probably, the most correct. (Though to be honest I never fully tried it)

Simple custom solution.

If you don't want to mess with TypeConverters, you can always use something like this, that just assumes a static Parse(String) method on your custom types:

public class Value { private readonly Int32 _value; public Value(Int32 value) { this._value = value; } public override String ToString() { return String.Format("Value {0}", this._value); } public static Value Parse(String str) { return new Value(Int32.Parse(str)); } } public class Converter { private readonly IDictionary<Type, Func<String, Object>> _parseFunctions; public Converter() { this._parseFunctions = new Dictionary<Type, Func<String, Object>>(); } public T Convert<T>(String str) { Func<String, Object> parse; if (this._parseFunctions.TryGetValue(typeof(T), out parse)) { return (T)parse(str); } var parseMethodInfo = typeof(T) .GetMethod( name: "Parse", bindingAttr: BindingFlags.Static | BindingFlags.Public, binder: null, types: new[] { typeof(String) }, modifiers: null); if (parseMethodInfo != null) { var parameters = new [] { Expression.Parameter(typeof(String)) }; parse = Expression .Lambda<Func<String, Object>>( Expression.Convert( expression: Expression.Call(null, parseMethodInfo, parameters), type: typeof(Object)), parameters: parameters) .Compile(); this._parseFunctions.Add(typeof(T), parse); return (T)parse(str); } return (T)System.Convert.ChangeType(str, typeof(T)); } } public static void Main() { try { var converter = new Converter(); Console.WriteLine(converter.Convert<Int32>("123")); Console.WriteLine(converter.Convert<Double>("123.32")); Console.WriteLine(converter.Convert<Value>("500")); Console.WriteLine(converter.Convert<Value>("600")); } catch (Exception exc) { Console.WriteLine(exc); } Console.WriteLine("Press any key..."); Console.ReadKey(true); } 
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer. I have to admit, I do not fully understand it (I am still new to C#), but I do like that it is lean. Unfortunately I have a problem with this solution, which likely has to do with my locale (german). The line Console.WriteLine(converter.Convert<Double>("123.32")); gives me the output 12332. When I change it to Console.WriteLine(converter.Convert<Double>("123,32")); I get 123,32. Is there a way to get your solution to work independently of the locale?
@packoman In such a case you can search for a Parse(String, IFormatProvider) instead of the Parse(String)' method and use CultureInfo.InvariantCulture` as its second parameter IFormatProvider.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.