Нормально ли не обрабатывать возвращаемое значение метода C#? Что такое хорошая практика в этом примере?


из любопытства...что происходит, когда мы вызываем метод, который возвращает некоторое значение, но мы не обрабатываем/используем его? И мы также ожидаем, что иногда это возвращаемое значение может быть очень большим. Куда идет это значение? Он вообще создан? Если это так, есть ли какие-либо проблемы с производительностью или другие проблемы, которые могут возникнуть? (какова наилучшая практика в такой ситуации?)

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

public static Datatable InsertIntoDB(...) 
{
      // executing db command, getting values, creating & returning Datatable object...
      ...
      return myDataTable;
}

и затем, когда этот метод используется, он называется так:

DataTable myDataTable = InsertIntoDB(...);
// this Datatable object is handled in some way

но иногда просто такой:

InsertIntoDB(...);
// returned value not handled; Problem???

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

10 64

10 ответов:

возвращенное значение (или ссылка, Если это ссылочный тип) помещается в стек, а затем снова выскакивает.

пустяки.

Если возвращаемое значение не имеет значения, вы можете безопасно сделать это.

но будьте уверены, что это не актуально, на всякий случай.

вот код:

    static string GetSomething()
    {
        return "Hello";
    }

    static void Method1()
    {
        string result = GetSomething();
    }

    static void Method2()
    {
        GetSomething();
    }

Если мы посмотрим на IL:

Метод1:

.locals init ([0] string result)
IL_0000:  nop
IL_0001:  call       string ConsoleApplication3.Program::GetSomething()
IL_0006:  stloc.0
IL_0007:  ret

метода Method2:

IL_0000:  nop
IL_0001:  call       string ConsoleApplication3.Program::GetSomething()
IL_0006:  pop
IL_0007:  ret

точно такое же количество инструкции. В методе 1 значение хранится в локальной строке результата (stloc.0), который удаляется, когда он выходит за рамки. В методе 2 Операция pop просто удаляет его из стека.

в вашем случае возвращения чего-то "действительно большого", эти данные уже созданы, и метод возвращает ссылку на него; не сами данные. В Method1 () ссылка назначается локальной переменной, и сборщик мусора будет очищать ее после того, как переменная вышла из строя сфера (конец метода в данном случае). В методе 2 () сборщик мусора может приступить к работе в любое время после извлечения ссылки из стека.

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

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

EDIT: немного смягчил язык и прояснил.

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

один пример, где я видел это так:

int foo;
int.TryParse(someText, out foo);

// Keep going

здесь foo будет 0, Если либо someText содержит "0", или он не может быть проанализирован. нам может быть все равно, что было дело в этом случае возвращаемое значение метода не имеет отношения к нам.

другой пример находится в словаре - предположим, вы пытаетесь подсчитать количество вхождений каждой строки. Вы можете использовать:

int count;
dictionary.TryGetValue(word, out count);
dictionary[word] = count + 1;

если слово не было в словаре для начала, это эквивалентно тому, что есть количество 0 - что уже произойдет в результате вызова TryGetValue.

в качестве встречного примера, игнорируя значение, возвращаемое Stream.Read (и предполагая, что это удалось прочитать все данные, которые вы просили) является распространенной ошибкой.

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

EDIT: другие примеры, где можно игнорировать возвращаемое значение:

  • некоторые свободные интерфейсы, в том числе StringBuilder, а StringBuilder.Append(x).Append(y); использует первое возвращаемое значение для второго вызова, очень часто возвращаемое значение вызова будет игнорироваться, например, при добавлении в цикл
  • некоторые вызовы коллекции могут давать возвращаемые значения, которые иногда игнорируются-например HashSet<T>.Add, который указывает, является ли значение на самом деле добавил, или уже присутствовал. Иногда тебе просто все равно.

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

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

в данном случае DataTable реализует IDisposable Так что это не все 100% нормально:

если возвращаемый объект реализует IDisposable тогда его хорошая идея, чтобы избавиться от него, например:

using (var retVal = InsertIntoDB(...))
{
    // Could leave this empty if you wanted
}

Это зависит от возвращаемого значения он сам.

компилятор будет генерировать это значение в методе звонящего, так что если значение IDispolable или подвергнуть Close метод или если у него есть ресурсы, которые должны быть освобождены, то вы не должны игнорировать его и утилизировать его должным образом, иначе вы можете страдать от проблем и утечек памяти..

например, если возвращаемое значение FileStream и вы не закрыли поток, файл не может быть закрыт, пока ваше приложение прекращено, более того, если ваше приложение попытается снова открыть файл, оно может выдать исключение, которое указывает "файл используется другим процессом". Поэтому вы должны быть осторожны с таким возвращаемым объектом и никогда не игнорируй это!

Это совершенно нормально, чтобы игнорировать возвращаемое значение.

возвращенное значение выбрасывается, если оно не используется, но создается. Совершенно разумно не использовать его (Хотя вы должны быть ceratin, что это правильно), но если для создания требуется много ресурсов, то это впустую.

вы можете рассмотреть, будет ли другой метод лучшим вариантом, который вообще не создает возвращаемый объект.

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

кроме того, это редко хорошая идея молча игнорировать возвращаемое значение. Читатели кода могут не иметь исходного контекста автора. Они могут подумать, что он просто забыл им воспользоваться. Если возвращаемое значение не является важным, лучше быть явным об этом решении:

var ignoredReturnValue = InsertIntoDB(...);

интересно, в Nemerle на самом деле дает вам предупреждение, если вы игнорировать возвращаемое значение. Чтобы не получить предупреждение, вы должны быть ясны о своем решении и написать:

_ = InsertIntoDB(...);

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

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

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

Если ваша функция делает некоторые изменения для других объектов (например, БД), я думаю, что это нормально, чтобы не обрабатывать возвращаемый объект, если он вам не нужен.

все эти разговоры о том, можно ли игнорировать возвращаемые типы, не нужны, мы все равно делаем это все время в C#. Многие функции, которые вы используете, как будто они возвращают пустоту, не возвращают пустоту. Подумайте об общей функции, такой как Button1.Фокус ()

знаете ли вы, что .Функция Focus () возвращает значение bool? Он возвращает true, если ему удалось сосредоточиться на элементе управления. Таким образом, вы можете проверить его как bool, сказав:

Если (кнопка button1.Фокус = = true) Почтовый ящик.Показать ("кнопка успешно сфокусирована."); еще Почтовый ящик.Показать ("не удалось сосредоточиться на кнопке, извините.");

но обычно вы этого не делаете. Ты просто говоришь: Кнопка button1.Фокус();

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

дело в том, что мы игнорируем возвращаемые значения все время, даже если вы этого не знаете.