Шаблон «TryParse/Разобрать как»:как лучше всего это реализовать

StackOverflow https://stackoverflow.com/questions/182440

Вопрос

Этот вопрос является продолжением Как указать, что метод не удался.Шаблон xxx() Tryxxx() может оказаться очень полезным во многих библиотеках.Мне интересно, как лучше всего предложить обе реализации без дублирования моего кода.

Что лучше:

public int DoSomething(string a)
{
     // might throw an exception
}
public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }

или

public int DoSomething(string a)
{
     int result;
     if (TrySomething(a, out result))
     {
         return result;
     }
     else
     {
         throw Exception(); // which exception?
     }
}
public bool TrySomething(string a, out result)
{
    //...
}

Я инстинктивно предположил бы, что первый пример более корректен (вы точно знаете, какое исключение произошло), но не может ли попытка/вылов быть слишком дорогой?Есть ли способ перехватить исключение во втором примере?

Это было полезно?

Решение

Заставить TrySomething просто перехватывать и проглатывать исключение — очень плохая идея.Половина смысла шаблона TryXXX — избежать снижения производительности из-за исключений.

Если вам не нужно много информации в исключении, вы можете заставить метод DoSomething просто вызывать TrySomething и генерировать исключение в случае сбоя.Если вам нужны подробности в исключении, вам может потребоваться что-то более подробное.Я не рассчитал, где находится основная часть снижения производительности исключений - если это выбрасывание, а не создание, вы могли бы написать закрытый метод, который имел бы сигнатуру, аналогичную TrySomething, но который возвращал бы исключение или ноль:

public int DoSomething(string input)
{
    int ret;
    Exception exception = DoSomethingImpl(input, out ret);
    if (exception != null)
    {
        // Note that you'll lose stack trace accuracy here
        throw exception;
    }
    return ret;
}

public bool TrySomething(string input, out int ret)
{
    Exception exception = DoSomethingImpl(input, out ret);
    return exception == null;
}

private Exception DoSomethingImpl(string input, out int ret)
{
    ret = 0;
    if (input != "bad")
    {
        ret = 5;
        return null;
    }
    else
    {
        return new ArgumentException("Some details");
    }
}

Однако засеките это, прежде чем приступить к этому!

Другие советы

Обычно я использую этот шаблон.Имеет ли это какой-либо смысл, зависит от того, как реализован метод Internal.Если вам приходится использовать условные блоки catch, это может стать немного неприятно...

public object DoSomething(object input){
  return DoSomethingInternal(input, true);
}

public bool TryDoSomething(object input, out object result){
  result = DoSomethingInternal(input, false);
  return result != null;
}

private object DoSomethingInternal(object input, bool throwOnError){
  /* do your work here; only throw if you cannot proceed and throwOnError is true */
}

Первый пример верен, если вы собираетесь просто перехватить исключение и не делать ничего, кроме возврата с ним false.

Вы можете изменить TrySomething, чтобы он выглядел так, как показано ниже.

public bool TrySomething(string a, out result, bool throwException)
{
  try
  {
    // Whatever
  }
  catch
  {
    if(throwException)
    {
      throw;
    }
    else
    {
      return false;
    }
  }

}

public bool TrySomething(string a, out result)
{
  return TrySomething(a, out result, false);
}

Итак, DoSomething будет выглядеть так

public int DoSomething(string a)
{
  int result;

  // This will throw the execption or 
  // change to false to not, or don't use the overloaded one.
  TrySomething(a, out result, true) 

  return result;      
}

Если вы не хотите, чтобы TrySomething с throwException был общедоступным, вы можете сделать его закрытым членом.

Исключения могут оказаться дорогостоящими, и вы можете выполнить некоторую проверку строки RegEx, чтобы предотвратить ее выдачу.Это зависит от того, что вы пытаетесь сделать.

Предполагая, что это C#, я бы сказал, что второй пример

public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

Он имитирует встроенный int.TryParse(string s, out int result), и, на мой взгляд, лучше всего оставаться в соответствии с языком/окружением.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top