可为空的通用可解析类

时间:2018-08-02 09:16:57

标签: c# class parsing

我正在尝试编写一个通用结构来解析可为空的类型。

我有几个问题:

  1. 我无法继承Nullable<T>,因为它是一个结构:

    public struct NullableParsable<T> : Nullable<T> where T : struct { }
    
  

接口列表中的T类型不是接口

所以我尝试:

    public struct NullableParsable<T> where T : struct
    {
        public static T? Parse(string s)
        {
            T? result = null;
            string tName = typeof(T).Name;
            switch(tName)
            {
                case "Int32": result = string.IsNullOrEmpty(s) ? null : (int?)int.Parse(s); break;
                case "Decimal": result = string.IsNullOrEmpty(s) ? null : (decimal?)decimal.Parse(s); break;
                default: throw new NotImplementedException("unmanaged type: "+ tName);
            }

            return result;
        }
    }
  1. 我无法将int投射到Tint?投射到T?

  2. 我想在结构类型(也许是一个枚举)之间进行切换的一种简单方法,暂时在类型名称typeof(T).Name之间进行切换。也许是反射机制来调用Parse ...

在此提琴中查看完整的代码:https://dotnetfiddle.net/x6kHzx

关于如何正确实现此功能的任何想法?

3 个答案:

答案 0 :(得分:1)

由于您将无法处理所有struct(在硬编码列表之外,没有Parse可以通过反射定位),因此我建议在{{1} },对于您知道如何支持这些类型的。例如

string

无论如何,您在这里都不会从泛型中受益,因为所有public static class ParseExtensions { public static int? ParseInt(this string input) { if(Int32.TryParse(input,out var result) { return result; } return null; } public static DateTime? ParseDateTime(this string input) { if(DateTime.TryParse(input, out var result) { return result; } return null; } } / Parse方法都是单独的方法,它们碰巧共享一个名称。他们没有共享的继承/接口祖先。

答案 1 :(得分:1)

除了已经给出的建议之外,您可以做的是使用字典而不是switch语句来使它更具动态性。这不会改变只能解析指定的类型。

例如,您可以创建一个知道如何解析这些类型的类:

public class NullableParsable
{
    private static readonly Dictionary<string, Func<string, object>> _parsers =
        new Dictionary<string, Func<string, object>>();

    public static void Register<T>(Func<string, T?> parser)
        where T : struct
    {
        var key = typeof(T).FullName;
        _parsers.Add(key, x => parser(x));
    }

    public static T? Parse<T>(string value)
        where T : struct
    {
        var key = typeof(T).FullName;
        if (_parsers.TryGetValue(key, out var parser))
        {
            if (string.IsNullOrEmpty(value))
            {
                return null;
            }

            return (T?) parser(value);
        }

        throw new NotSupportedException("Not sure how to map this type");
    }
}

然后,指定如何解析特定类型:

NullableParsable.Register<int>(s => int.Parse(s));
NullableParsable.Register<decimal>(s => decimal.Parse(s));
NullableParsable.Register<Guid>(s => Guid.Parse(s));

用法示例:

int? result1 = NullableParsable.Parse<int>("123");
decimal? result2 = NullableParsable.Parse<decimal>("123");
Guid? result3 = NullableParsable.Parse<Guid>(Guid.NewGuid().ToString("D"));

答案 2 :(得分:-1)

实际上,我只需要将结果声明为dynamic,然后就可以为其分配一个Nullable并将其返回为T?

public static T? Parse(string s)
{
    dynamic result = null;
    int intValue;
    result = int.TryParse(s, out intValue) ? (int?)intValue : null;
    return result;
}

有了关系,我可以调用Parse:

public static T? Parse(string s)
{
    dynamic result = null;
    Type type = typeof(T);
    string tName = type.Name;
    MethodInfo methodInfo = type.GetMethod("Parse", new[] { typeof(string) });
    result = methodInfo.Invoke(null, new[] { s });
    return result;
}

更好,我可以调用TryParse以避免try / catch:

public static T? Parse(string s)
{
    Type type = typeof(T);
    string tName = type.Name;
    MethodInfo methodInfo = type.GetMethod("TryParse", new[] { typeof(string), type.MakeByRefType() });
    object[] args = new object[] { s, null };
    bool parsed = (bool)methodInfo.Invoke(null, args);
    dynamic result = parsed ? args[1] : null;
    return result;
}

更新了fiddle

相关文章: How to call TryParse dynamically?Generic TryParse