В C #, где вы используете «ref» перед параметром? - PullRequest
18 голосов
/ 30 апреля 2009

Существует ряд вопросов по определению параметров ref и out, но они кажутся плохими. Есть ли случаи, когда вы считаете, что ref - это правильное решение?

Кажется, вы всегда можете сделать что-то более чистое. Может кто-нибудь дать мне пример, где это было бы «лучшим» решением проблемы?

Ответы [ 10 ]

11 голосов
/ 30 апреля 2009

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

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

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

9 голосов
/ 30 апреля 2009

Ну, ref обычно используется для специализированных случаев, но я бы не назвал это избыточной или устаревшей функцией C #. Вы увидите, что он (и out) часто используется в XNA, например. В XNA Matrix является struct и довольно массивным в этом отношении (я считаю, 64 байта), и, как правило, лучше, если вы передадите его функциям, использующим ref, чтобы избежать копирования 64 байтов, но только 4 или 8 Специалист по C #? Конечно. Из мало пользы больше или свидетельствует о плохом дизайне? Я не согласна

7 голосов
/ 30 апреля 2009

В одной области используются небольшие вспомогательные функции, такие как:

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }  

Я не вижу здесь более «чистых» альтернатив. Конечно, это не совсем уровень архитектуры.

4 голосов
/ 30 апреля 2009

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

3 голосов
/ 30 апреля 2009

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

void GetXYZ( ref object x, ref object y, ref object z);

EDIT: divo , предложенное с использованием параметров OUT, будет более подходящим для этого Я должен признать, у него есть точка. Я оставлю этот ответ здесь, для протокола, это неадекватное решение. В этом случае OUT превосходит REF.

1 голос
/ 30 апреля 2009

Один шаблон проектирования, где ref - это двунаправленный посетитель.

Предположим, у вас есть класс Storage, который можно использовать для загрузки или сохранения значений различных примитивных типов. Это либо в режиме Load, либо в режиме Save. У него есть группа перегруженных методов с именем Transfer, и вот пример для работы с int значениями.

public void Transfer(ref int value)
{
    if (Loading)
        value = ReadInt();
    else
        WriteInt(value);
}

Были бы аналогичные методы для других примитивных типов - bool, string и т. Д.

Тогда для класса, который должен быть "переносимым", вы должны написать метод, подобный этому:

public void TransferViaStorage(Storage s)
{
    s.Transfer(ref _firstName);
    s.Transfer(ref _lastName);
    s.Transfer(ref _salary);
}

Этот же единственный метод может загружать поля из Storage или сохранять поля в Storage, в зависимости от того, в каком режиме находится объект Storage.

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

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

Сравнение: атрибуты + отражение

Почему это вместо того, чтобы приписывать поля и использовать отражение для автоматической реализации эквивалента TransferViaStorage? Потому что иногда рефлексия достаточно медленная, чтобы быть узким местом (но всегда быть профильной, чтобы быть уверенным в этом - это вряд ли когда-либо верно, а атрибуты намного ближе к идеалу декларативного программирования).

1 голос
/ 30 апреля 2009

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

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

1 голос
/ 30 апреля 2009

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

0 голосов
/ 24 апреля 2012

Существует один очевидный случай, когда вы должны использовать ключевое слово 'ref'. Если объект определен, но не создан вне области метода, который вы намереваетесь вызвать И метод, который вы хотите вызвать, должен создать 'new' для его создания, вы должны использовать 'ref'. например, {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} не будет ничего делать с object 'a' и не будет жаловаться на это ни во время компиляции, ни во время выполнения. Это просто ничего не сделает. {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} приведет к тому, что 'a' будет новым объектом с именем "dummy ". Но если 'new' уже сделано, то ref не нужен (но работает, если он указан). {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}

0 голосов
/ 30 апреля 2009

Очевидная причина использования ключевого слова ref заключается в том, что вы хотите передать переменную по ссылке. Например, передача типа значения, такого как System.Int32, методу и изменение его фактического значения. Более конкретное использование может быть, когда вы хотите поменять местами две переменные.

public void Swap(ref int a, ref int b)
{
   ...
}

Основная причина использования ключевого слова out состоит в том, чтобы возвращать несколько значений из метода. Лично я предпочитаю переносить значения в специализированную структуру или класс, поскольку использование параметра out приводит к довольно уродливому коду. Параметры, передаваемые с помощью «out» - точно так же, как «ref» - ​​передаются по ссылке.

public void DoMagic(out int a, out int b, out int c, out int d)
{
   ...
}
...