Почему некоторые функции библиотеки C # не следуют соглашению о передаче параметров «ref» - PullRequest
0 голосов
/ 03 октября 2018

Есть много примеров, давайте возьмем метод копирования массива в качестве примера.Подпись метода Array.Copy is выглядит следующим образом:

public static void Copy (Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length);

Судя только по подписи, нельзя сказать, что sourceArray не будет изменен, пока будет изменен destinationArray, даже если это такая простая вещь, как массив Int. Гарантия, исходящая от ключевого слова "ref" для программистов, здесь потеряна.

Мне кажется, что параметр destinationArray лучше пометить как "ref Array".Если бы это было сделано таким образом, синтаксис был бы более согласованным с использованием ключевого слова «ref», указывающего, что переданный объект может быть изменен вызываемым объектом, и изменение является видимым для вызывающего.Единственное преимущество, которое я могу придумать в отношении подбора ключевого слова «ref», - это сохранение нескольких нажатий клавиш.или просто имитирует стиль C / C ++, не задумываясь.

Мой вопрос: какие приправы стоят за этим дизайнерским решением?

Обновление: для записи я выступаю за то, чтобы массив относился к той же категории значения / ссылки, что и его элементы, тем самым делая явное вымирание между Fun (массив) и Fun (массив ссылок), то естьТакую же гарантию получают программисты с Fun (int) и Fun (ref int).Оптимизацию для эффективности можно оставить на уровне реализации.

Ответы [ 3 ]

0 голосов
/ 03 октября 2018

Причина пропуска ключевого слова ref заключается в том, что в большинстве случаев его не имеет никакого значения, поэтому это излишне.Однако в некоторых случаях это действительно имеет значение.Массив является ссылочным типом, и это означает, что значение, представляющее эту ссылку, передается.Как правило, обновление переданного значения вызывает обновления исходного объекта.НО, если вы создаете НОВЫЙ массив и назначаете переданный входной параметр новому элементу, ссылка теряется - тогда как ключевое слово ref сохраняет его.

0 голосов
/ 03 октября 2018

C # (и .NET) включают как ссылочные типы, так и типы значений.

Обычно (отсутствуют ключевые слова ref или out), параметры передаются в методы by value.Таким образом, если вы передаете целое число в функцию, передается значение целого числа.Если вы поместите переменную, ссылающуюся на массив, в вызове функции (помня, что все массивы являются экземплярами ссылочного типа System.Array), значение этой переменной, то есть ссылка на массив, передается функции.

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

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

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

В вашем примере вызывающая сторона создаст два массива одного типа и совместимых длин(обычно одинаковой длины, если исходный и целевой индексы равны 0, а длина равна sourceArray.Length).Функция не изменяет объект, на который ссылается параметр массива назначения, она просто заполняет пункт назначения из источника.

Фактически, если пункт назначения был by ref, он не был бы таким гибким.Рассмотрим случай, когда длина назначения составляет 30 записей, и вы намерены заполнить середину десяти записей массива источником.Это просто работает.Это не было бы с ref параметром назначения (без большой дополнительной работы).

0 голосов
/ 03 октября 2018

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

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

class Foo { public int Value; }

public static void ReplaceFoo(ref Foo foo)
{
    foo = new Foo { Value = 2 };
}

var foo = new Foo { Value = 1 };
Console.WriteLine(foo.Value);
ReplaceFoo(ref foo);
Console.WriteLine(foo.Value);

Судя только по подписи, нельзя сказать, что sourceArray не изменится, пока будет изменен массив destinationArray

Почему это проблема?Никто не читает API только обращая внимание на сигнатуры методов и игнорируя имена параметров.Сигнатуры существуют для компилятора, чтобы различать перегрузки.Любой, кто читает API для Array.Copy(), поймет, что sourceArray останется неизменным, поскольку метод получает значения от него, а destinationArray будет изменен, получая значения - если они нене говорят по-английски (что нормально, но большинство API написаны на английском языке).

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

...