3

构建了一个自定义 IDataReader,它在 XML 中查找与特定元素名称匹配的值,如果找到,则返回这些值。GetValue 函数——它必须返回一个由接口指定的对象,如下所示:

    public object GetValue(int i)
    {
        string SearchString = _columns[i];

        var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
        if (searchedAttributeValue.Count() > 0)
        {
            return ParseTypes(searchedAttributeValue.First().Value);
        }

        var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
        if (searchedElementValue.Count() > 0)
        {
            return ParseTypes(searchedElementValue.First().Value);
        }
    }

显然,这不会构建,因为在没有有效返回的情况下可能会到达函数的末尾。

在这种情况下,如果发生这种情况,则意味着存在配置错误 - 用户正在寻找 XML 中不存在的元素或属性。

我发现(这对我来说是新的)你可以通过这样做来解决这个问题:

    public object GetValue(int i)
    {
        if (_el == null)
        {
            _el = XNode.ReadFrom(_reader) as XElement;
        }
        string SearchString = _columns[i];

        var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
        if (searchedAttributeValue.Count() > 0)
        {
            return ParseTypes(searchedAttributeValue.First().Value);
        }

        var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
        if (searchedElementValue.Count() > 0)
        {
            return ParseTypes(searchedElementValue.First().Value);
        }

        throw new Exception("oh crap!");
    }

所以这个函数不会返回,它只是抛出一个错误。

但是,这种方法从根本上感觉有些错误。可以吗,为什么可以/不可以,是否有更好的方法来处理这种情况?

4

5 回答 5

6

好吧,除非你真的需要这个catch块,否则你不需要引入 try/catch/finally。您可以throw在初始方法的末尾添加一条语句。

在此过程中,我将开始使用FirstOrDefault(),遵循局部变量的正常 C# 命名约定,在查询表达式不是非常有用时停止使用它们,并抛出更具体的异常:

public object GetValue(int i)
{
    string searchString = _columns[i];

    var searchedAttribute = _el.Attributes(searchString).FirstOrDefault();
    if (searchedAttribute != null)
    {
        return ParseTypes(searchedAttribute.Value);
    }

    var searchedElement = _el.Elements(searchString).FirstOrDefault();
    if (searchedElement != null)
    {
        return ParseTypes(searchedElement.Value);
    }
    // Nothing found - oops.
    throw new SomeSpecificException("Some message here");
}

如果您需要 catch 块进行日志记录,我可能会尝试捕获一个特定的异常,这可能意味着无论如何都要将其移入ParseTypes(因为您不应该从其余的调用中得到任何异常......)无论哪种方式,保持最后的throw声明。

编辑:在设计方面,是否应该在找不到值时抛出异常实际上取决于期望。我们无法知道这是否表明系统有问题,或者只是完全正常的数据缺失。这应该决定你在这里的选择。特别是,如果每个调用者都试图捕获异常,那么这是一种设计味道,您应该返回null或使用类似Dictionary<,>.TryGetValue方法的东西。

于 2013-11-13T14:56:48.407 回答
2

我喜欢使我的方法类似于 .NET 框架 Parse() 和 TryParse() 方法。在你的情况下,我会这样做:

public object GetValue(int i)
{
    // ...

    // fail
    throw new Exception("Cannot get value");
}

或者做:

public bool TryGetValue(int i, out object result)
{
    // ...

    // fail
    result = null;
    return false;
}
于 2013-11-13T15:00:01.577 回答
1

try catch 是无关紧要的。您可以简单地将代码重构为:

public object GetValue(int i)
{

   if (_el == null)
   {
      _el = XNode.ReadFrom(_reader) as XElement;
   }
     string SearchString = _columns[i];

   var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
   if (searchedAttributeValue.Count() > 0)
   {
      return ParseTypes(searchedAttributeValue.First().Value);
   }

   var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
   if (searchedElementValue.Count() > 0)
   {
      return ParseTypes(searchedElementValue.First().Value);
   }

   throw new Exception("oh crap!");
}

这具有相同的最终结果。


也就是说,抛出异常是昂贵的(计算上)。如果你想让它爆炸(完全停止处理,这是一个主要问题,它应该简单地死掉)然后抛出一个异常。

如果它只是一种识别何时if不满足语句的方法,则可以将其更改为TryParse类型函数:

public bool GetValue(int i, out object returnVal)
{

   if (_el == null)
   {
      _el = XNode.ReadFrom(_reader) as XElement;
   }
     string SearchString = _columns[i];

   var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
   if (searchedAttributeValue.Count() > 0)
   {
      returnVal = ParseTypes(searchedAttributeValue.First().Value);
      return true;
   }

   var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
   if (searchedElementValue.Count() > 0)
   {
      returnVal = ParseTypes(searchedElementValue.First().Value);
      return true;
   }

   return false;
}

然后

if (GetValue(i, out value))
   //success
else
   //it's failed.
于 2013-11-13T14:55:36.743 回答
1

如果真的是异常情况,因为配置错误导致搜索不返回任何东西,那么一定要抛出异常。问题在于继续。如果找不到项目,客户端代码可以继续吗?如果可以,客户端代码将需要仔细检查您的代码引发的任何异常,以确定它是因为项目不存在还是因为其他无法继续的错误而引发的。

另一种选择是返回一些指示未找到该值的内容,并允许调用代码来处理它。您可以返回null以指示调用代码未找到任何项目,在这种情况下可能没问题。更好的解决方案可能是创建一个简单的Optional<T>类,其中包含对象是否存在(可能HasValue)以及对象本身(如果存在)的指示。调用代码可以轻松更具体地检查对象是否被返回并处理没有返回的情况,并且异常不需要额外的审查。

编辑:一个更好的选择可能是其他人建议ParseTryParse范式,但我认为这个答案可能有一些用处,所以我把它留在这里:)

于 2013-11-13T15:03:48.377 回答
0

这种方法有很多不好的做法:catch (Exception)应始终避免。只捕获您期望的异常。此外,throw Exception()是糟糕的风格。总是抛出一些更具体的东西。但是对于您要查找的内容: 毕竟,您不需要 finally,只需将 athrow new ArgumentException()或其他内容作为函数的最后一行。这是一个很好的做法,如果代码曾经在那里运行,它确实是一个编程错误。

于 2013-11-13T14:57:42.237 回答