【问题标题】:How to parse a string value to a custom type using a generic function如何使用泛型函数将字符串值解析为自定义类型
【发布时间】:2016-02-24 04:20:50
【问题描述】:

我正在尝试编写一个类来将带有值字符串的 XML 配置文件解析为相应的值和类型。配置的示例条目是:

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

我现在想写一个函数来解析我的值,这样我就可以做这样的事情:

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

最初我尝试使用其返回类型重载GetItemValue,但这在 C# 中是不可能的。我现在尝试使用通用函数,这样我就可以做到:

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

我尝试写的函数是这样的:

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);
}

不幸的是,这不起作用,我收到以下错误:

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

我不确定问题出在哪里,但我怀疑,这与编译时不清楚的事实有关,是否所有类型T 都会有方法TryParse。我尝试使用 where T : Int, Double 之类的约束来解决此问题,但 C# 约束不支持 ValueTypes。

那么问题是什么,我该如何实现,我想要做什么?请注意任何建议,将来我还必须为 T 自定义类型。

【问题讨论】:

  • 如果您将int.TryParse() 用于您的T.TryParse() 方法,则可能有错误。 int.TryParse() 使用为当前系统文化初始化的 NumberFormatInfo 对象中的格式信息解析数字。 但是,XML 通常是独立于文化的。我建议使用与文化无关的方法,或来自XmlConvert 的方法。

标签: c# xml parsing generics


【解决方案1】:

标准类型。

假设您只需要标准的内置原始类型,您可以使用Convert 或类似的东西。

使用自定义类型的正确方法。

但是如果你需要支持自定义类型,那么你可以使用TypeConverter。这是一种笨重的方式,但可能是最正确的方式。 (虽然老实说我从来没有完全尝试过)

简单的自定义解决方案。

如果您不想弄乱TypeConverters,您可以随时使用这样的东西,它只是在您的自定义类型上假设一个静态Parse(String) 方法:

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);         
}

【讨论】:

  • 感谢您的回答。我不得不承认,我并不完全理解它(我还是 C# 的新手),但我确实喜欢它很精简。不幸的是,我对这个解决方案有疑问,这可能与我的语言环境(德语)有关。 Console.WriteLine(converter.Convert&lt;Double&gt;("123.32")); 行给了我输出 12332。当我将其更改为Console.WriteLine(converter.Convert&lt;Double&gt;("123,32")); 时,我得到123,32。有没有办法让您的解决方案独立于区域设置?
  • @packoman 在这种情况下,您可以搜索Parse(String, IFormatProvider) 而不是Parse(String)' method and use CultureInfo.InvariantCulture` 作为其第二个参数IFormatProvider
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-08
  • 2019-02-15
  • 2020-03-17
相关资源
最近更新 更多