Что плохого в параметрах ref? - PullRequest
14 голосов
/ 20 февраля 2009

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

Это заставляет меня думать: "Вау, сумасшедший, должен найти другой путь". Я глупый? Какие проблемы могут быть вызваны параметром ref?

Редактировать

Были запрошены дополнительные детали, я не думаю, что они полностью соответствуют тому, что я просил, но здесь мы идем.

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

Код может прояснить ситуацию:

protected override void BeforeSave(Log entity)
{
    var newLog = entity;

    var existingLog = (from log in repository.All()
                           where log.Stuff == newLog.Stuff 
                                 && log.Id != newLog.Id
                           select log).SingleOrDefault();

    if (existingLog != null)
    {
        // update the time
        existingLog.SomeValue = entity.SomeValue;
        // remove the reference to the new entity
        entity = existingLog;
    }
}

// called from base class which usually does nothing before save
public void Save(TEntity entity)
{
    var report = validator.Validate(entity);

    if (report.ValidationPassed)
    {
        BeforeSave(entity);
        repository.Save(entity);
    }
    else
    {
        throw new ValidationException { Report = report };
    }
}

Тот факт, что я добавил бы его только для одного (пока) дочернего элемента базового класса, препятствует использованию перегрузки (из-за того, что мне пришлось бы дублировать метод Save). У меня также есть проблема, из-за которой мне нужно заставить их использовать версию ref в этом случае, иначе все будет работать не так, как ожидалось.

Ответы [ 12 ]

28 голосов
/ 20 февраля 2009

Можете ли вы добавить перегрузку? Есть одна подпись без параметра ref и одна с ним.

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

13 голосов
/ 20 февраля 2009

Возможно, используйте перегруженную функцию для этого 5% case и оставьте другую функцию как есть.

Ненужные параметры ссылки могут привести к неправильным шаблонам проектирования, но если у вас есть особые потребности, не проблема с этим.

5 голосов
/ 20 февраля 2009

Если вы берете .NET Framework в качестве барометра ожиданий людей от API, учтите, что почти все методы String возвращают измененное значение , но оставляют переданный аргумент без изменений. Например, String.Trim () возвращает обрезанную строку - она ​​не обрезает строку, переданную в качестве аргумента.

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

В конечном счете, все зависит от вас и от того, как вы документируете свой API. Однако по своему опыту я обнаружил, что мои коллеги-программисты склонны ожидать, что мои функции будут действовать «как функции .NET Framework». :)

4 голосов
/ 20 февраля 2009
Параметр

A ref сам по себе не вызовет проблем. Это документированная особенность языка.

Однако это может вызвать социальные проблемы. В частности, клиенты вашего API могут не ожидать параметр ref просто потому, что он встречается редко. Вы можете изменить ссылку, которую клиент не ожидает.

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

2 голосов
/ 20 февраля 2009

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

Одна вещь, которая может быть рассмотрена, - это уменьшить ваши страхи по поводу параметра ref с помощью другого типа параметра. Например, рассмотрим это:

public class SaveArgs
{
   public SaveArgs(TEntity value) { this.Value = value; }

   public TEntity Value { get; private set;}
   public int NewId { get; internal set; }
   public bool NewIdGenerated { get; internal set; } 
}

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

Просто мысль.

РЕДАКТИРОВАТЬ: Исправлен код. Мой плохой.

1 голос
/ 24 сентября 2015

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

  • Иногда будет лучше использовать перегруженный метод.

  • Остальным будет лучше вернуть пользовательский тип или кортеж.

  • Другим будет лучше использовать глобальную переменную.

  • И другие ссылки будут правильным решением.

1 голос
/ 20 февраля 2009

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

1 голос
/ 20 февраля 2009

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

Большую часть времени я использую параметр ref, это в сценарии TryGet. В общем, я перестал использовать ref в этом сценарии и вместо этого выбрал использование более функционального метода стиля в качестве опции.

Например. TryGetValue в словаре переключается с

bool TryGetValue(TKey key, out TValue value)

К

Option<Value> TryGetValue(TKey key)

Опция доступна здесь: http://blogs.msdn.com/jaredpar/archive/2008/10/08/functional-c-providing-an-option-part-2.aspx

1 голос
/ 20 февраля 2009

Нет ничего плохого в том, чтобы использовать параметры ref, о которых я могу думать, на самом деле они иногда очень удобны. Я думаю, что они иногда получают плохой рэп из-за отладки, поскольку значение переменной может измениться в логике кода, а иногда может быть трудно отследить. Это также затрудняет преобразование в такие вещи, как WebServices, для которых достаточно только прохода «значение».

0 голосов
/ 20 февраля 2009

Функция, возвращающая пустоту, с одним опорным параметром, конечно, выглядит смешно. Если бы я просматривал этот код, я бы предложил рефакторинг BeforeSave (), чтобы включить вызов Repository.Save () - переименовав его, очевидно. Почему бы просто не иметь один метод, который берет ваш возможно новый объект и гарантирует, что все сохранено правильно? В любом случае вызывающая сторона ничего не делает с возвращенным объектом.

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