如果出现解析错误,我将如何设计提供详细信息的TryParse方法?


9

解析用户输入时,通常建议不要抛出和捕获异常,而应使用验证方法。在.NET BCL中,这将是例如int.Parse(在无效数据上引发异常)和int.TryParse(返回false无效数据)之间的区别。

我正在设计自己的

Foo.TryParse(string s, out Foo result)

方法,我不确定返回值。我可以用bool像.NET自己的TryParse方法,但不会给有关指示类型的错误,有关的确切原因,为什么 s不能被解析成Foo。(例如,s括号可能不匹配,或者字符数错误,或者Bar没有对应的Baz等)。

作为用户的API,我强烈不喜欢它就会返回一个成功/失败布尔瞒着我的方法为什么操作失败。这使得调试猜测游戏成为可能,我也不想将其强加给图书馆的客户。

我可以想到很多解决此问题的方法(返回状态代码,返回错误字符串,将错误字符串添加为out参数),但是它们都有各自的缺点,我也想保持与.NET Framework

因此,我的问题如下:

.NET Framework中是否存在与简单的true / false布尔值相比,(a)解析输入而不抛出异常,并且(b)仍返回更详细的错误信息的方法?


1
该链接并未得出结论,不建议抛出并捕获异常。有时,最好的方法是使用Parse()
狗仔队

Answers:


5

我建议您为返回类型使用monad模式。

ParseResult<Foo> foo = FooParser.Parse("input");

还请注意,Foo不应负责弄清楚应如何从用户输入中解析它,因为这直接将您的域层绑定到您的UI层,并且还违反了单一责任主体。

Foo根据您的用例,您还可以使解析结果类特定于而不是使用泛型。

特定于foo的解析结果类可能看起来像这样:

class FooParseResult
{
     Foo Value { get; set; }
     bool PassedRequirement1 { get; set; }
     bool PassedRequirement2 { get; set; }
}

这是Monad版本:

class ParseResult<T>
{
     T Value { get; set; }
     string ParseErrorMessage { get; set; }
     bool WasSuccessful { get; set; }
}

我不知道.net框架中的任何返回详细分析错误信息的方法。


我了解您对UI层绑定的评论,但是在这种情况下,存在Foo的标准化,规范化字符串表示形式,因此具有Foo.ToStringand 有意义Foo.Parse
Heinzi

而且,我的粗体问题是,您能举一个使用此模式的.NET BCL中的示例吗?
Heinzi

4
那单子怎么样?
JacquesB

@Heinzi:Func<T>如果T您需要的信息中包含任何返回a的方法,则将满足该条件。返回详细的错误信息完全取决于您。您是否考虑过使用Maybe<T>?参见mikhail.io/2016/01/monads-explained-in-csharp
罗伯特·哈维

@JacquesB:我有点想知道同一件事。方法签名与现代行为兼容,仅此而已。
罗伯特·哈维

1

您可以在MVC框架中查看ModelState。它表示尝试解析某些输入,并且可能包含错误集合。

就是说,我认为.net BCL中没有这种重复发生的模式,因为无论是好是坏,异常都是-报告.net错误情况的既定模式。我想你应该先走一步,实现自己的解决方案相适应的问题,例如,ParseResult有两个子类,SuccessfulParse并且FailedParse,其中SuccessfulParse与解析值的属性,并FailedParse有一个错误消息属性。将此与C#7中的模式匹配结合起来可能会非常优雅。


1

我已经想使用一个碰到类似的问题TryParse/Convert/etc.,我的方法有时需要知道如何和为什么会失败。

我最终从一些序列化程序如何处理错误和使用事件中获得灵感。这样,我TryX(..., out T)方法的语法看起来就和其他语法一样清晰,并且可靠地返回了false模式所暗示的简单语法。

但是,当我需要更多详细信息时,只需添加一个事件处理程序,然后在所需的复杂或简单包中获取所需的任何结果(MyEventArgs如下所示)。将其添加到字符串列表,添加ExceptionDispatchInfo和捕获Exception;让呼叫者决定是否以及如何处理任何出错的地方。

public class Program
{
    public static void Main()
    {
        var c = new MyConverter();

        //here's where I'm subscibing to errors that occur
        c.Error += (sender, args) => Console.WriteLine(args.Details);

        c.TryCast<int>("5", out int i);
    }
}

//here's our converter class
public class MyConverter
{
    //invoke this event whenever something goes wrong and fill out your EventArgs with details
    public event EventHandler<MyEventArgs> Error;

    //intentionally stupid implementation
    public bool TryCast<T>(object input, out T output)
    {
        bool success = true;
        output = default (T);

        //try-catch here because it's an easy way to demonstrate my example
        try
        {
            output = (T)input;
        }
        catch (Exception ex)
        {
            success = false;
            Error?.Invoke(this, new MyEventArgs{Details = ex.ToString()});
        }

        return success;
    }
}

//stores whatever information you want to make available
public class MyEventArgs : EventArgs
{
    public string Details {get; set;}
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.