C#类方法。如何失败和回报为什么?

C#类方法。如何失败和回报为什么?,c#,.net,error-handling,C#,.net,Error Handling,我确信有一个“好”的方法来解决这个问题,但这一直困扰着我。我有一个方法,应该返回一个对象,但它的参数有一定的先决条件。这些都在我的控制范围之外,并且可能由于“业务逻辑”的原因而失败(如果您能原谅这个陈旧的术语的话) 该对象的返回值将为null,但我还想返回原因,因此调用代码本质上可以说“我没有得到我的对象,因为没有足够的信息来构建它” 我不认为try-catch是正确的方法,但在这种情况下,由于缺乏更好的方法,我一直在使用try-catch。我在这里阅读的有关stackoverflow、教科书和

我确信有一个“好”的方法来解决这个问题,但这一直困扰着我。我有一个方法,应该返回一个对象,但它的参数有一定的先决条件。这些都在我的控制范围之外,并且可能由于“业务逻辑”的原因而失败(如果您能原谅这个陈旧的术语的话)

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

我不认为try-catch是正确的方法,但在这种情况下,由于缺乏更好的方法,我一直在使用try-catch。我在这里阅读的有关stackoverflow、教科书和MSDN的所有内容似乎都集中在何时或如何使用异常上,但不知何故,我没有找到解决这种情况的方法

有人能提出一个更合适的模式吗?(第一篇帖子如此…请原谅任何失礼)

下面是一个示例:(注意//TODO注释下面的抛出新异常行)

publicstaticpacketparse(stringpacketstring)
{
数据包返回数据包=新数据包();
StringBuilder输出=新的StringBuilder();
尝试
{
使用(XmlReader=XmlReader.Create(newstringreader(packetString)))
{
XmlWriterSettings ws=新的XmlWriterSettings();
ws.Indent=true;
使用(XmlWriter=XmlWriter.Create(输出,ws))
{
string rootNodeString=string.Empty;
//解析数据包字符串并捕获每个节点。
while(reader.Read())
{
//测试根节点是正确的打开节点名称
if(rootNodeString==string.Empty)
{
if(reader.NodeType!=XmlNodeType.Element | | reader.Name!=PACKETROOT)
{
//TODO:我真的认为这不应该是一个例外,但为了方便起见,现在就使用它,因为XmlReader也在做同样的事情
抛出新异常(string.Format(“数据包的根节点必须是”,PACKETROOT));
}
其他的
{
rootNodeString=reader.Name;
}
}
开关(reader.NodeType)
{
case XmlNodeType.Element:
WriteLine(string.Format(“start元素={0}”,reader.Name));
打破
案例XmlNodeType.Text:
WriteLine(string.Format(“text={0}”,reader.Value));
打破
案例XmlNodeType.XmlDeclaration:
案例XmlNodeType.ProcessingInstruction:
WriteLine(string.Format(“XmlDeclaration/ProcessingInstruction={0},{1}”,reader.Name,reader.Value));
打破
案例XmlNodeType.Comment:
WriteLine(string.Format(“comment={0}”,reader.Value));
打破
案例XmlNodeType.EndElement:
WriteLine(string.Format(“end元素={0}”,reader.Name));
打破
}
}
}
}
}
捕获(XmlException xem)
{
控制台写入线(xem.Message);
投掷;
}
返回包;
}

如果您测试的内容“不应该发生”,那么使用异常正是您应该做的。毕竟,这是一个例外情况。为什么您不应该使用一个呢?

例外情况适用于此。不清楚您为什么会这样想…

典型的模式是始终返回一个int,这是错误代码。实际结果通过
out
ref
参数传递。一些开发工厂确实需要这样做作为必要条件。

我还没有真正用C#开发,但根据我使用其他语言(Java、PHP)的经验,我认为这是一种非常好的方法。如果要区分抛出的错误和XMLReader抛出的错误,可以创建自己的自定义异常,扩展异常对象。

通过在存在无效输入时抛出异常,本质上是说调用方法负责确保数据有效,或者处理异常(如果不是)。这是完全可以接受的,尽管在理想情况下应该将其纳入方法文档中

如果您正在这样做,最好有一个方法来验证输入;通过提供此选项,调用方可以知道,如果不需要遭受try/catch的性能影响,该方法将无法工作。如果该类与一个或几个其他类紧密耦合,而这些类总是为它提供正确的输入,那么这就变得不那么重要了(但这仍然不是一个坏主意)。

我使用的方法是拥有一个类型,它是解析操作的结果。它知道它是否成功,如果请求解析失败,它将抛出一个异常,否则它不会抛出异常。(如果不抛出异常,您当前无法获取将抛出的异常,但我稍后可能会添加该异常。)

这比抛出一个异常要好,因为这里预期会失败——这并不是真正的异常

它比使用
int.TryParse
等模式更好,因为它为您提供了一个单一的结果值,封装了有关解析操作的所有内容—不再需要处理out参数,而是
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;
}
    public class ActionResult<T>
    {
        public T Result { get; set; }
        public bool IsSuccessful { get; set; }
        public string DetailMessage { get; set; }

        //Any other properties you might find useful like statuscode, etc.
    }

    public ActionResult<Packet> ParsePacket(string input)
    {
        Packet result = null;
        bool parseSuccess = true;
        string message = null;

        // Do your work, create packet and check conditions. 
        // Assign to your local variables.

        return new ActionResult<Packet> { 
            Result = result, 
            IsSuccessful = parseSuccess, 
            DetailMessage = message 
        };
    }

    public void SomeCallingMethodLikeGetPacket(string userInput)
    {
        ActionResult<Packet> parseResult = ParsePacket(userInput);
        if (!parseResult.IsSuccessful)
        {
            //Error handling.
        }
        else
        {
            Packet packet = parseResult.Result;
            //Do something with packet.
        }
    }