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

В С#, где вы используете "ref" перед параметром?

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

Я не вижу альтернативы "чище". Конечно, это не совсем уровень архитектуры.

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

Один шаблон проектирования, в котором 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? Потому что иногда отражение достаточно медленно, чтобы быть узким местом (но всегда профиль, чтобы быть уверенным в этом - он вряд ли когда-либо верен, а атрибуты гораздо ближе к идеалу декларативного программирования).

Ответ 9

Очевидная причина использования ключевого слова "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)
{
   ...
}

Ответ 10

Есть один ясный случай, когда вы должны использовать ключевое слово '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";}