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

Что плохого в параметрах ref?

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

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

Edit

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

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

Код может быть более понятным:

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 в этом случае, иначе все будет работать не так, как ожидалось.

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

Параметр

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

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

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

Ответ 5

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

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

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

EDIT: Исправлен код. Мой плохой.

Ответ 6

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

Ответ 7

Самая большая проблема с параметрами 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

Ответ 8

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

Ответ 9

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

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

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

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

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

Ответ 10

Если вашему методу нужен только этот параметр ref 5% времени, возможно, вам нужно сломать этот метод. Конечно, без каких-либо подробностей трудно сказать, но это для меня пахнет случаем нарушения принципа единственной ответственности. Возможно, перегрузка этого поможет.

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

Ответ 11

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

Я понятия не имею, является ли это плохой практикой или существует какая-то базовая "booga booga" о параметрах ref, для меня они просто чувствуют, что это не чистый синтаксис.

Ответ 12

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