Шаблон «TryParse / Parse like»: как лучше всего его реализовать - PullRequest
8 голосов
/ 08 октября 2008

Этот вопрос является продолжением Как указать, что метод был неудачным . Шаблон 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)
{
    //...
}

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

Ответы [ 4 ]

14 голосов
/ 08 октября 2008

Сделать 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");
    }
}

Время, прежде чем совершить это, хотя!

3 голосов
/ 08 октября 2008

Я обычно использую этот шаблон. Зависит от того, как реализован метод 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 */
}
2 голосов
/ 08 октября 2008

Предполагая, что это 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), и, на мой взгляд, лучше всего соответствовать языку / среде.

2 голосов
/ 08 октября 2008

Первый пример верен, если вы просто собираетесь перехватить исключение и ничего не делать, а вернуть с ним 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 для строки, чтобы предотвратить ее выброс. Это зависит от того, что вы пытаетесь сделать.

...