Вопрос

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

Я видел подобный метод следующим образом,

   public void Do(int arg1, int arg2, out int result)

есть ли какие-нибудь случаи, когда это действительно имеет смысл?

как насчет TryParse, почему бы не вернуть ParseResult тип?или в более новом фреймворке возвращать тип с нулевым значением?

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

Решение

Выходить на улицу хорошо, когда у вас есть TryNNN функция, и ясно, что out-параметр всегда будет установлен, даже если функция не завершится успешно.Это позволяет вам полагаться на тот факт, что объявленная вами локальная переменная будет установлена вместо того, чтобы позже устанавливать проверки в вашем коде на значение null.(Комментарий ниже указывает, что параметр может быть установлен в null, поэтому вы можете захотеть проверить документацию для вызываемой вами функции, чтобы убедиться, так это или нет.) Это делает код немного понятнее и его легче читать.Другой случай - это когда вам нужно вернуть некоторые данные и статус при условии метода, например:

public bool DoSomething(int arg1, out string result);

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

Недостатком является то, что вам нужно объявить локальную переменную, чтобы использовать их:

string result;
if (DoSomething(5, out result))
    UpdateWithResult(result);

Вместо того , чтобы:

UpdateWithResult(DoSomething(5));

Однако это может быть даже не недостатком, это зависит от дизайна, к которому вы стремитесь.В случае DateTime предоставляются оба средства (Parse и TryParse).

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

Ну, как и в большинстве вещей, это зависит.Давайте рассмотрим возможные варианты

  • вы могли бы вернуть все, что захотите, в качестве возвращаемого значения функции
  • если вы хотите вернуть несколько значений или функция уже имеет возвращаемое значение, вы можете либо использовать out params, либо создать новый составной тип, который предоставляет все эти значения в качестве свойств

В случае TryParse использование параметра out эффективно - вам не нужно создавать новый тип, который потребовал бы 16B накладных расходов (на 32b машинах) или нести дополнительные расходы на сбор мусора после вызова.TryParse может быть вызван, например, из цикла - так что здесь правило out params .
Для функций, которые не были бы вызваны внутри цикла (т.е.производительность не является серьезной проблемой), возврат одного составного объекта может быть "более чистым" (субъективным для наблюдателя).Теперь с анонимными типами а динамический ввод текста, возможно, станет еще проще.

Примечание:

  1. out у параметров есть некоторые правила, которым необходимо следовать, т. е.компилятор гарантирует, что функция действительно инициализирует значение перед своим завершением.Таким образом, TryParse должен присвоить параметру out некоторое значение, даже если операция синтаксического анализа завершилась неудачей
  2. Шаблон TryXXX является хорошим примером того, когда следует использовать out params - Int32.TryParse был представлен, потому что люди жаловались на перехват исключений, чтобы узнать, не произошел ли сбой синтаксического анализа.Также наиболее вероятная вещь, которую вы сделали бы в случае успешного анализа, - это получить проанализированное значение - использование параметра out означает, что вам не нужно вызывать другой метод для Parse

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

bool isValid = int.TryParse("100", out int result = 0);

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

public void Do(int arg1, int arg2, out int result)

Нет особого смысла использовать параметр out, поскольку вы возвращаете только одно значение, и этот метод можно было бы использовать лучше, если бы вы удалили параметр out и ввели возвращаемое значение int:

public int Do(int arg1, int arg2)

Есть несколько хороших моментов в out parameters:

  1. Выходные параметры изначально считаются неназначенными.
    • Каждый выходной параметр должен если вы определенно назначены до возврата метода, ваш код не будет компилироваться, если вы пропустите назначение.

В заключение, я в основном пытаюсь использовать out params в моем частный API чтобы избежать создания отдельных типов для переноса нескольких возвращаемых значений, и в моем общедоступном API я использую их только для методов, которые соответствуют шаблону TryParse.

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

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

Параметры Out более просты в использовании.

Да, в этом действительно есть смысл.Возьмем это, к примеру.

String strNum = "-1";
Int32 outNum;

if (Int32.TryParse(strNum, out outNum)) {
    // success
}
else {
    // fail
}

Что вы могли бы вернуть, если бы операция завершилась с ошибкой в обычной функции с возвращаемым значением?Вы, безусловно, не могли бы вернуть -1, чтобы представить сбой, потому что тогда не было бы различия между значением, возвращаемым при сбое, и фактическим значением, которое анализировалось с самого начала.Вот почему мы возвращаем логическое значение, чтобы увидеть, удалось ли это, и если это произошло, то наше "возвращаемое" значение уже безопасно присвоено.

Меня действительно раздражает, что я не могу передать значение null в параметр out для функций TryParse.

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

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

Как уже было сказано здесь, одним из типичных вариантов использования является TrySomething Метод, в котором вы хотите вернуть bool в качестве индикатора успеха, а затем фактическое значение.Я также нахожу, что в операторе if немного чище - все три варианта в любом случае имеют примерно одинаковый LOC.

int myoutvalue;
if(int.TryParse("213",out myoutvalue){
    DoSomethingWith(myoutvalue);
}

vs.

ParseResult<int> myoutvalue = int.TryParse("213");
if ( myoutvalue.Success ) {
    DoSomethingWith(myoutvalue.Value);
}

vs.

int? myoutvalue = int.TryParse("213");
if(myoutvalue.HasValue){
    DoSomethingWith(myoutvalue.Value);
}

Что касается вопроса "Почему бы не вернуть обнуляемый тип":TryParse существует начиная с Framework 1.x, тогда как типы с нулевым значением появились в версии 2.0 (поскольку для них требуются дженерики).Так зачем же без необходимости нарушать совместимость или начинать вводить несоответствия между TryParse для некоторых типов?Вы всегда можете написать свой собственный метод расширения, чтобы дублировать уже существующую функциональность (см. Сообщение Эрика Липпертса на несвязанную тему, которая включает в себя некоторые рассуждения о том, что делать / не делать)

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

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

StatusInfo a, b, c;

Initialize(out a);
Validate(a, out b);
Process(b, out c);

против.

StatusInfo a = Initialize();
StatusInfo b = Validate(a);
StatusInfo c = Process(b);

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

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