Подтвердить что ты не робот

Реальные примеры, где параметры С# 'out' полезны?

Я читаю строчные конструкторы программирования С# и с трудом переворачиваю голову вокруг модификатора параметра out. Я знаю, что он делает, читая, но я пытаюсь думать о scenerio, когда буду использовать его.

Может ли кто-нибудь дать мне реальный пример? Спасибо.

4b9b3361

Ответ 1

существует много сценариев, в которых вы бы использовали его, но основным будет то, где ваш метод должен возвращать более одного параметра. Возьмем, например, методы TryParse для типа int. В этом случае вместо того, чтобы бросать исключение, bool возвращается как флаг success/failure, а parsed int возвращается как параметр out. если вы должны были вызвать int.Parse(...), вы могли бы создать исключение.

string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}

Ответ 2

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

Обычно у вас есть только один механизм для возврата значений, возвращаемое значение функции. Конечно, вы можете использовать глобальные (статические) или переменные экземпляра, но это не очень практично и безопасно вообще (по причинам, которые я не буду здесь объяснять). До .NET 3.5 не было действительно практического способа вернуть несколько значений из функции. Если модификаторы out или ref недоступны, у вас будет несколько вариантов:

  • Если все ваши значения имеют один и тот же тип, вы можете вернуть некоторую коллекцию значений. В большинстве случаев это прекрасно, вы можете вернуть массив чисел, список строк, что угодно. Это идеально, если все значения были связаны точно так же. то есть все числа были количеством элементов в контейнере, или список имел имена гостей на вечеринке. Но что, если возвращаемые вами ценности представляли разные количества? Что, если у них были разные типы? Список объектов может содержать их все, но это не очень интуитивный способ манипулирования данными такого рода.

  • В случае, когда вам нужно возвращать несколько значений разных типов, единственным практическим вариантом было создание нового типа class/struct для инкапсуляции всех этих значений и возврата экземпляра этого типа. Таким образом, вы можете вернуть строго типизированные значения с интуитивными именами, и вы можете вернуть несколько значений таким образом. Проблема в том, что для этого вам нужно было определить тип с определенным именем и все, чтобы иметь возможность возвращать несколько значений. Что делать, если вы хотите вернуть только два значения, которые были достаточно просты, что делает невозможным создание для него типа? У вас еще есть несколько вариантов:

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

    • Опция, которая использовалась, заключалась в включении модификатора out, который позволяет вызывающему передать "ссылку" на переменную, чтобы функция могла установить ссылочную переменную как другой способ возврата значения. Этот способ возвращения значений также был доступен на C и С++ разными способами по тем же причинам и сыграл определенную роль в влиянии на это решение. Однако разница в С# заключается в том, что для параметра out функция должна установить значение на что-то. Если это не так, это приводит к ошибке компилятора. Это делает это менее подверженным ошибкам, поскольку, имея параметр out, вы обещаете вызывающему, что вы установите значение на что-то, и они могут его использовать, компилятор убедится, что вы придерживаетесь этого обещания.

Заметка о типичном использовании модификатора out (или ref) будет редко увидеть более одного или двух параметров out. В таких случаях почти всегда будет идея создания инкапсулирующего типа. Вы обычно использовали бы его, если бы вам понадобилось еще одно значение для возврата.

Однако, поскольку С# -3.0/.NET-3.5 с введением анонимных типов и кортежей, внедренных в .NET 4.0, эти опции предоставили альтернативные методы, позволяющие упростить (и более интуитивно понятный) несколько значений различных типов.

Ответ 3

Конечно, взгляните на любой из методов TryParse, например int.TryParse:

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

Использование:

string input = Console.ReadLine();
int value;

// First we check the return value, which is a bool
// indicating success or failure.
if (int.TryParse(input, out value))
{
    // On success, we also use the value that was parsed.
    Console.WriteLine(
        "You entered the number {0}, which is {1}.",
        value,
        value % 2 == 0 ? "even" : "odd"
    );
}
else
{
    // Generally, on failure, the value of an out parameter
    // will simply be the default value for the parameter's
    // type (e.g., default(int) == 0). In this scenario you
    // aren't expected to use it.
    Console.WriteLine(
        "You entered '{0}', which is not a valid integer.",
        input
    );
}

Многие разработчики жалуются на параметры out как "запах кода"; но они могут быть, безусловно, наиболее подходящим выбором во многих сценариях. Одним очень важным современным примером будет многопоточный код; часто параметр out необходим для разрешения "атомных" операций, где недопустимое значение возврата.

Рассмотрим, например, Monitor.TryEnter(object, ref bool), который получает блокировку и атомарно устанавливает a bool, что было бы невозможно с помощью только возвращаемого значения, поскольку сбор блокировки обязательно произойдет до того, как возвращаемое значение будет присвоено значению bool. (Да, технически ref и out не совпадают, но они очень близки).

Другим хорошим примером могут быть некоторые из методов, доступных для классов коллекции в пространстве имен System.Collections.Concurrent, новых для .NET 4.0; они обеспечивают аналогичные поточно-безопасные операции, такие как ConcurrentQueue<T>.TryDequeue(out T) и ConcurrentDictionary<TKey, TValue>.TryRemove(TKey, out TValue).

Ответ 4

Параметры вывода находятся по всей платформе .NET. Некоторые из видов использования, которые я вижу чаще всего, это методы TryParse, которые возвращают логическое значение (указывающее, действительно ли синтаксический анализ был действителен) и фактический результат возвращается через выходной параметр. Хотя это также очень распространенное место для использования класса, когда вам нужно возвращать несколько значений, в таком примере, как это, это немного тяжело. Подробнее о выходных параметрах см. В статье Джона Скита о Передача параметров в С#.

Ответ 5

Простой, когда у вас есть метод, который возвращает более одного значения. Одним из самых "знаменитых" случаев является Dictionary.TryGetValue:

string value = "";

if (openWith.TryGetValue("tif", out value))
{
    Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
    Console.WriteLine("Key = \"tif\" is not found.");
}

Ответ 6

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

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

Пример того, что вам, возможно, пришлось написать для достижения той же функциональности, приведен здесь:

/// <summary>
/// Example code for how <see cref="int.TryParse(string,out int)"/> might be implemented.
/// </summary>
/// <param name="integerString">A string to convert to an integer.</param>
/// <param name="result">The result of the parse if the operation was successful.</param>
/// <returns>true if the <paramref name="integerString"/> parameter was successfully 
/// parsed into the <paramref name="result"/> integer; false otherwise.</returns>
public bool TryParse(string integerString, out int result)
{
    try
    {
        result = int.Parse(integerString);
        return true;
    }
    catch (OverflowException)
    {
        // Handle a number that was correctly formatted but 
        // too large to fit into an Int32.
    }
    catch (FormatException)
    {
        // Handle a number that was incorrectly formatted 
        // and so could not be converted to an Int32.
    }

    result = 0; // Default.
    return false;
}

Два исключений исключений, которые здесь можно избежать, делают код вызова более читаемым. Я считаю, что фактические реализации .NET полностью исключают исключения, поэтому лучше работают. Аналогично, этот пример показывает, как IDictionary.TryGetValue(...) делает код более простым и эффективным:

private readonly IDictionary<string,int> mDictionary = new Dictionary<string, int>();

public void IncrementCounter(string counterKey)
{
    if(mDictionary.ContainsKey(counterKey))
    {
        int existingCount = mDictionary[counterKey];

        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

public void TryIncrementCounter(string counterKey)
{
    int existingCount;
    if (mDictionary.TryGetValue(counterKey, out existingCount))
    {
        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

И все благодаря параметру out.

Ответ 7

bool Int32.TryParse(String, out Int);

или что-то подобное, например Dictionary.TryGetValue.

Но я бы счел, что это не слишком хорошая практика, чтобы использовать его, конечно, используя исключения, предоставляемые API, такие как Int32, чтобы избежать Try-Catch. Исключение составляют.

Ответ 8

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

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

Первая попытка (ошибочная)

Рассмотрим следующий интерфейс:

interface IUndoableCommand
{
    void Execute();
    IUndoableCommand GetUndoCommand();
}

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

someCommand.GetUndoCommand().Execute();
someCommand.Execute();
// ^ this is obviously wrong, but will compile.

Вторая попытка (все еще испорчена)

Итак, постарайтесь предотвратить эту ошибку, переконструировав интерфейс:

interface IUndoableCommand
{
    IUndoableCommand Execute();
}

Теперь у вас есть доступ к команде "отменить" после того, как команда отменена.

var undoCommand = someCommand.Execute();
undoCommand.Execute();
// ^ this is better, but has other problems...

Хотя этот дизайн явно не плох, он страдает двумя недостатками:

  • Не очень логично для метода Execute возвращать что-то. Если он вообще что-то вернет, вы, вероятно, подумаете, что это какой-то индикатор успеха ( "result" ), но не другая команда "отменить".

  • Это позволяет вам "цепочки" вызовов Execute в форме:

    someCommand.Execute().Execute();
    //         ^^^^^^^^^^^^^^^^^^^^
    //        guess what this does!?
    

    В отличие от того, как он выглядит, someCommand не будет выполняться дважды; он будет эффективно отменен.

Окончательная попытка с параметром out (успешно)

Итак, попробуйте улучшить интерфейс, заменив возвращаемое значение параметром out:

interface IUndoableCommand
{
    void Execute(out IUndoableCommand undoCommand);
}

Эта конструкция не имеет ни одного из недостатков, показанных выше:

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

    IUndoableCommand undoCommand;
    someCommand.Execute(out undoCommand);
    undoCommand.Execute(… /* out someCommand */);
    

(Конечно, как только у вас есть ссылки на обе команды, вы все равно можете делать вред, например, дважды называть undoCommand, но, вероятно, нет способа остановить это во время компиляции.)

Ответ 9

//out key word is used in function instead of return. we can use multiple parameters by using out key word
public void outKeyword(out string Firstname, out string SecondName)
{
    Firstname = "Muhammad";
    SecondName = "Ismail";

}
//on button click Event
protected void btnOutKeyword_Click(object sender, EventArgs e)
{
    string first, second;
    outKeyword(out first, out second);
    lblOutKeyword.Text = first + "  " + second;
}