Когда лучше использовать ключевое слово C # ref? - PullRequest
29 голосов
/ 22 августа 2010

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

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

Я не мудрый разработчик; Я уверен, что это имеет практически применимое использование. Я просто хотел бы знать, что они.

Ответы [ 10 ]

33 голосов
/ 22 августа 2010

Руководство по проектированию рамок (книга Кшиштофа Квалины и Брэда Абрамса) рекомендует избегать параметров ref и out.

AVOID с использованием параметров out или ref.

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

В Руководстве по проектированию платформы канонический метод Swap приводится в качестве допустимого исключения:

void Swap<T>(ref T obj1, ref T obj2)
{
    T temp = obj1;
    obj1 = obj2;
    obj2 = temp;
}

но в то же время комментарий замечает

Swap всегда встречается в этих обсуждениях, но я не писал код, который действительно нуждался в методе swap начиная с колледжа. Если у вас нет веских причин, избегайте out и ref в целом.

13 голосов
/ 22 августа 2010

Большинство Interlocked методов используют ref параметры для (я уверен, что вы согласны) веской причины.

10 голосов
/ 22 августа 2010

Я стараюсь избегать этого в общедоступных API, но он определенно имеет применение.Изменяемые значения-типы являются важными, особенно в таких вещах, как CF (где изменяемые структуры более распространены из-за требований платформы).Тем не менее, пожалуй, самое распространенное время, которое я использую, - это рефакторинг частей сложного алгоритма в несколько методов, когда объект состояния избыточен, и мне нужно передать несколько значений:1004 *

и т. Д.Где DoSomethingSpecific - это закрытый метод, который был перемещен, чтобы сохранить ответственность за метод.

4 голосов
/ 22 августа 2010

Каждый раз, когда вы хотите изменить значение типа value - это часто случается в тех случаях, когда вы хотите эффективно обновить пару связанных значений (то есть вместо возврата структуры, содержащей два целых числа, Вы проходите (ref int x, ref int y))

2 голосов
/ 22 августа 2010

Может быть, когда у вас есть структура (которая является типом значения):

struct Foo
{
    int i;

    public void Test()
    {
        i++;
    }
}

static void update(ref Foo foo)
{
    foo.Test();
}

и

Foo b = new Foo();
update(ref b);

Здесь вы можете использовать два параметра с out, например:

static void update(Foo foo, out Foo outFoo) //Yes I know you could return one foo instead of a out but look below
{
    foo.Test();

    outFoo = foo;
}

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

Добавление: Различные .TryParse методы также могли бы избежать out, если бы они вернули Nullable<T>вместо этого, который по сути является кортежем boolean * T.

0 голосов
/ 15 апреля 2015

Другой полезный пример в дополнение к swap <>:

Prompter.getString("Name ? ", ref firstName);
Prompter.getString("Lastname ? ", ref lastName);
Prompter.getString("Birthday ? ", ref firstName);
Prompter.getInt("Id ? ", ref id);
Prompter.getChar("Id type: <n = national id, p = passport, d = driver licence, m = medicare> \n? ", ref c);



public static class Prompter
{
    public static void getKey(string msg, ref string key)
    {
        Console.Write(msg);
        ConsoleKeyInfo cki = Console.ReadKey();
        string k = cki.Key.ToString();
        if (k.Length == 1)
            key = k;
    }

    public static void getChar(string msg, ref char key)
    {
        Console.Write(msg);
        key = Console.ReadKey().KeyChar;
        Console.WriteLine();
    }

    public static void getString(string msg, ref string s)
    {
        Console.Write(msg);
        string input = Console.ReadLine();
        if (input.Length != 0)
            s = input;
    }

    public static void getInt(string msg, ref int i)
    {
        int result;
        string s;

        Console.Write(msg);
        s = Console.ReadLine();

        int.TryParse(s, out result);
        if (result != 0)
            i = result;       
    }

    // not implemented yet
    public static string getDate(string msg)
    {
        // I should use DateTime.ParseExact(dateString, format, provider);
        throw new NotImplementedException();
    }    


}

Использовать здесь, это не вариант

0 голосов
/ 22 августа 2010

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

0 голосов
/ 22 августа 2010

Гипотетически, я бы предположил, что вы могли бы использовать множество аргументов ref / out, если вы намеревались имитировать архитектуру старого процедурного программного обеспечения, например, старые игровые движки и так далее. Я отсканировал исходный код одного, я думаю, что это был Duke Nukem 3D, и он процедурный с большим количеством подпрограмм, изменяющих переменные, и почти без функций. Очевидно, что вы вряд ли запрограммируете подобное для реального производственного приложения, если не будете иметь в виду какую-то конкретную цель.

0 голосов
/ 22 августа 2010

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

0 голосов
/ 22 августа 2010

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

Пример:

 getTreeNodeValues(ref selectedValue, ref selectedText);

Edit:

Лучше использовать здесь - как прокомментировано.

 getTreeNodeValues(out selectedValue, out selectedText);

Я использую его для обработки объектов:

MyCar car = new MyCar { Name="TestCar"; Wieght=1000; }

UpdateWeight(ref car, 2000);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...