Чтобы спросить разрешение или извиниться?

Я родом из питона, где часто говорят, что легче извиниться, чем спрашивать разрешения. Специально дано два фрагмента:

if type(A) == int:
  do_something(A)
else:
  do_something(int(A))

try:
  do_something(A)
except TypeError:
  do_something(int(A))

Тогда в большинстве сценариев использования второй будет быстрее, когда A обычно является целым числом (при условии, что do_something требует целое число в качестве входных данных и вызовет его исключение довольно быстро), так как вы теряете логический тест из каждого цикла выполнения за счет более дорогостоящее исключение, но гораздо реже.

Я хотел проверить, так ли это в C#, или же логические тесты достаточно быстры по сравнению с исключениями, чтобы сделать это маленьким угловым случаем?

Ох, и меня интересует только выпуск релиза, а не отладка.


ОК, мой пример был слишком расплывчатым, попробуйте этот:

Наивное решение:

return float(A) % 20 # coerse A to a float so it'll only fail if we actually don't
                     # have anything that can be represented as a real number.

Логическое решение:

if isinstance(A, Number): # This is cheaper because we're not creating a new
    return A % 20         # object unless we really have to.
else:
    return float(A) %20

Решение на основе исключений:

try: # Now we're doing any logical tests in the 99% of cases where A is a number
  return A % 20
except TypeError:
  return float(A) % 20

Примеры использования FSO, соединений с базами данных или чего-то другого по сети лучше, но немного скучны для вопроса.

5 ответов

Решение

Возможно нет. Исключения.NET относительно дороги.

По этой причине несколько функций.NET предлагают оба варианта. (int.TryParse, который возвращает код успеха, часто рекомендуется, потому что он быстрее, чем int.Parse который выдает исключение при неудаче)

Но единственный ответ, который имеет значение, - это то, что говорят ваши собственные данные профилирования. Если вам нужна производительность, то вам нужно измерить, измерить, измерить.

Потому что то, что было самым быстрым на моем компьютере, с моим кодом, с моей версией.NET Framework, в настоящее время может быть не самым быстрым на вашем компьютере, с вашим кодом, с вашей версией.NET Framework в то время, когда вы прочитай это.

Исключения в.NET довольно тяжелые, поэтому философия в C# - использовать исключения только для исключительных ситуаций, а не для выполнения программы.

Философия C# также направлена ​​на проверку всего ввода, полученного из внешнего кода, перед его использованием. Пример:

public void Foo(int i)
{
    if (i == 0)           // validate input received from external code
    {
        throw new ArgumentOutOfRangeException("i");
    }

    DoSomething(i);
}

public void Foo()
{
    DoSomething(1);
}

internal void DoSomething(int i)
{
    Debug.Assert(i != 0); // validate that i is not zero in DEBUG build
                          // assume that i is not zero in RELEASE build

    Console.WriteLine(42 / i);
}

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

С точки зрения производительности есть разница в сборке релиза - разумные логические тесты, безусловно, достаточно быстрые - так что лично я бы пошел на удобочитаемость.

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

Во всяком случае, я думаю, что ваш вопрос слегка ошибочный, пришедший из python. C# является (или был?) Языком со статической типизацией, что означает, что многие сценарии, аналогичные тем, которые вы предлагаете, могут быть разрешены во время компиляции.

http://paltman.com/2008/01/18/try-except-performance-in-python-a-simple-test/ имеет аналогичный тест, за исключением того, что смотрит на has_key, который я бы ожидал (слегка) дороже, чем проверка типов.

В случае большого количества итераций, когда ключ существует (поэтому исключение не выдается), он примерно на 25% быстрее, но все еще довольно быстр. Там, где ключ никогда не существует, он примерно на 1000% медленнее.

Теперь, имея в виду, что проверка типов выполняется быстрее, чем поиск ключа, и что исключения.Net, как уже упоминалось выше, являются достаточно тяжелыми, вам понадобится A, чтобы быть целым числом в подавляющем большинстве случаев, прежде чем оно даже станет потенциально полезным.

Но, как упоминал ранее Джалф. Попробуйте и посмотрите.

Другие вопросы по тегам