C#类方法。如何失败和回报为什么?
我确信有一个“好”的方法来解决这个问题,但这一直困扰着我。我有一个方法,应该返回一个对象,但它的参数有一定的先决条件。这些都在我的控制范围之外,并且可能由于“业务逻辑”的原因而失败(如果您能原谅这个陈旧的术语的话) 该对象的返回值将为null,但我还想返回原因,因此调用代码本质上可以说“我没有得到我的对象,因为没有足够的信息来构建它” 我不认为try-catch是正确的方法,但在这种情况下,由于缺乏更好的方法,我一直在使用try-catch。我在这里阅读的有关stackoverflow、教科书和MSDN的所有内容似乎都集中在何时或如何使用异常上,但不知何故,我没有找到解决这种情况的方法 有人能提出一个更合适的模式吗?(第一篇帖子如此…请原谅任何失礼) 下面是一个示例:(注意//TODO注释下面的抛出新异常行)C#类方法。如何失败和回报为什么?,c#,.net,error-handling,C#,.net,Error Handling,我确信有一个“好”的方法来解决这个问题,但这一直困扰着我。我有一个方法,应该返回一个对象,但它的参数有一定的先决条件。这些都在我的控制范围之外,并且可能由于“业务逻辑”的原因而失败(如果您能原谅这个陈旧的术语的话) 该对象的返回值将为null,但我还想返回原因,因此调用代码本质上可以说“我没有得到我的对象,因为没有足够的信息来构建它” 我不认为try-catch是正确的方法,但在这种情况下,由于缺乏更好的方法,我一直在使用try-catch。我在这里阅读的有关stackoverflow、教科书和
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.
}
}