C#类方法.如何失败并返回原因?

reu*_*ben 5 .net c# error-handling

我确信有一种解决这个问题的"好方法",但它总是让我感到烦恼.我有一个应该返回一个对象的方法,但它的参数有一定的前提条件.这些是我无法控制的,可能因"业务逻辑"原因而失败(如果你原谅这个陈旧的术语).

该对象的返回值将为null但我也想传回原因,因此调用代码基本上可以说"我没有得到我的对象,因为没有足够的信息来构建它".

我不觉得尝试捕获是正确的方法,但在这种情况下一直使用它需要更好的方法.我在stackoverflow和教科书以及MSDN上的所有阅读似乎都集中在何时如何使用异常,但我在某种程度上未能为这种情况提出一种方法.

任何人都可以建议一种更合适的模式吗?(第一篇帖子......请原谅任何失礼)

以下是我通过示例一直在玩的示例:(请注意在// TODO注释下面抛出新的Exception行)

public static Packet Parse(string packetString)
{
    Packet returnPacket = new Packet();
    StringBuilder output = new StringBuilder();

    try
    {
        using (XmlReader reader = XmlReader.Create(new StringReader(packetString)))
        {
            XmlWriterSettings ws = new XmlWriterSettings();
            ws.Indent = true;
            using (XmlWriter writer = XmlWriter.Create(output, ws))
            {
                string rootNodeString = string.Empty;

                // Parse the packet string and capture each of the nodes.
                while (reader.Read())
                {
                    //test root node is the correct opening node name
                    if (rootNodeString == string.Empty)
                    {
                        if (reader.NodeType != XmlNodeType.Element || reader.Name != PACKETROOT)
                        {
                            // TODO: I don't really think this should be an exception, but going with it for now for expediency, since XmlReader is doing the same anyway
                            throw new Exception(string.Format("The root node of a Packet must be <{0}>", PACKETROOT));
                        }
                        else
                        {
                            rootNodeString = reader.Name;
                        }
                    }

                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            Console.WriteLine(string.Format("start element = {0}", reader.Name));
                            break;
                        case XmlNodeType.Text:
                            Console.WriteLine(string.Format("text = {0}", reader.Value));
                            break;
                        case XmlNodeType.XmlDeclaration:
                        case XmlNodeType.ProcessingInstruction:
                            Console.WriteLine(string.Format("XmlDeclaration/ProcessingInstruction = {0},{1}", reader.Name, reader.Value));
                            break;
                        case XmlNodeType.Comment:
                            Console.WriteLine(string.Format("comment = {0}", reader.Value));
                            break;
                        case XmlNodeType.EndElement:
                            Console.WriteLine(string.Format("end element = {0}", reader.Name));
                            break;
                    }
                }

            }
        }

    }
    catch (XmlException xem)
    {
        Console.WriteLine(xem.Message);
        throw;
    }

    return returnPacket;
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 11

我有一个应该返回一个对象的方法,但它的参数有一定的前提条件.这些是我无法控制的,可能因"业务逻辑"原因而失败

如果前置条件确实是一个先决条件,那么不遵守前提条件的调用者就会出现错误.正确的做法是让被调用者抛出一个异常,理想情况下是一个积极崩溃整个过程的异常.这将强烈鼓励调用者修复他们的bug.

但根据你的描述,听起来你所拥有的并不是一个先决条件; 前提条件必须是真实的,如果它们并非总是如此,那么调用者就是错误的.听起来你所拥有的是一种做得太多的方法; 它验证所提供的参数是否被某些业务策略归类为有效,并且如果参数有效则计算结果.

因此,我倾向于将其分为两种方法.

第一种方法分析其参数以查看业务策略是否将它们分类为有效,并返回报告对象,该对象详细描述参数有效或无效的原因.

第二种方法计算结果.第二种方法的前提条件是第一种方法可以验证参数.

该对象的返回值将为null但我也想传回原因,因此调用代码基本上可以说"我没有得到我的对象,因为没有足够的信息来构建它".

再一次,这是你试图做太多事情的更多证据.您希望方法的返回值既是描述未满足业务规则的原因的分析,也是业务流程的结果.这是两个非常不同的东西,因此应该通过两种不同的方法来计算.


Jon*_*eet 7

我在Noda Time中使用的方法是使用一个ParseResult<T>类型,这是解析操作的结果.它知道它是否成功,如果你要求解析失败,它会抛出异常,但不会抛出异常.(您目前无法得到哪些异常没有它被扔扔,但我可以补充一点以后.)

这比抛出异常更好,因为这里预计失败- 这并不是特别的.

它比使用int.TryParseetc 的模式更好,因为它为你提供了一个单独的结果值,它封装了解析操作的所有内容 - 不再混淆输出参数,以及在失败的情况下可用的更多细节.

现在我不清楚这是否适用于您的特定情况 - 但是当您基本上处理可能无效的数据时,它合适的(IMO),并且不会表明任何部分出现任何错误.你的系统:没有什么令人惊讶特殊的事情发生在这里,只是有人为你提供了错误的输入:(


Mat*_*nen 5

如果您正在测试"不应该发生",那么使用异常就是您应该做的.毕竟这是一种特殊情况.为什么你认为你不应该使用它?