Используя ref с классом С#

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

Должен ли я использовать ключевое слово ref для этого?

Я несколько озадачен тем, где использовать ключевые слова ref и out в С#, поскольку все классы распределяются динамически в куче, и мы фактически используем указатели для большинства операций.
Конечно, ключевые слова out и ref имеют смысл для примитивов и структур.

Кроме того, если я не отправляю список напрямую, но отправляю класс, содержащий список? (это internal и необходимо), мне все еще нужно использовать ref? или если я передаю его между функциями, ex:

void A(ref LinkedList<int> list){
    B(list);
}

void B(ref LinkedList<int> list){
    _myList = list;
}

Ответ 1

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

Ответ 2

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

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

Чтобы процитировать наиболее важные части этой страницы:

Параметры значения:

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

Контрольные параметры:

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

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

Ответ 3

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

Пример # 1: ref ключевое слово не обязательно.

// ...
   List myList = new List();
   PopulateList(myList);
// ...
void PopulateList(List AList)
{
   AList.Add("Hello");
   AList.Add("World");
}

Пример # 2: ref ключевое слово.

// ...
   List myList;
   PopulateList(ref myList);
// ...
void PopulateList(ref List AList)
{
   AList = new List();
   AList.Add("Hello");
   AList.Add("World");
}

Ответ 4

Нет, вам не нужно использовать ref.

LinkedList - это объект, поэтому он уже является ссылочным типом. Параметр list является ссылкой на объект LinkedList.

См. эту статью статью MSDN для описания типов значений. Типы значений обычно являются параметрами, с которыми вы бы использовали ключевые слова ref или out.

Вы также можете передать ссылочные типы ref. Это позволит вам указать ссылку на другой объект.

Всякий раз, когда вы передаете object o, вы действительно передаете ссылку на объект. Когда вы передаете `ref object o ', вы передаете ссылку на ссылку. Это позволяет вам изменить ссылку.

Передача параметров ссылочного типа также может помочь вам понять.

Ответ 5

В двух отрывках, которые вы отправили, нет необходимости передавать список по ссылке. Чтобы процитировать Jon Skeet, ссылки на объекты передаются по значению. Это означает, что вы захотите указать ссылочный тип, когда метод будет или может изменить ссылку на объект, и вы хотите, чтобы эта новая ссылка вернулась к вызывающему методу. Например:

void methodA(string test)
{
    test = "Hello World";
}

void methodB(ref string test)
{
    test = "Hello World";
}

void Runner()
{
    string first= "string";
    methodA(first);
    string second= "string";
    methodB(ref second);
    Console.WriteLine((first == second).ToString()); //this would print false
}

Ответ 6

Я добавляю этот ответ для программистов, которые используются для С++, как и я.

Классы, интерфейсы, делегаты и массивы reference types, что означает, что они имеют базовый указатель. Обычные вызовы функций копируют этот указатель (ссылку) по значению, а отправка по ссылке посылает ссылку на эту ссылку:

//C# code:
void Foo(ClassA     input)
void Bar(ClassA ref input)

//equivalent C++ code:
void Foo(ClassA*  input)
void Bar(ClassA*& input)

Примитивы, такие как int, double и т.д. структуры и строки (строка является исключением из них, но работает аналогично), выделяются в куче, поэтому все работает немного иначе:

//C# code:
void Foo(StructA     input)
void Bar(StructA ref input)

//equivalent C++ code:
void Foo(StructA  input)
void Bar(StructA& input)

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

//C# code:
void Foobar(ClassB ref input)
...
ClassB instance = new ClassB();
Foobar(ref instance);

//equivalent C++ code:
void Foobar(ClassB*& input)
...
ClassB instance* = new ClassB();
Foobar(instance);

Как уже было сказано, прочитайте это подробное объяснение. Он также объясняет строки.



Интересно отметить, что вызов по ссылке работает с основным указателем, поэтому мы добираемся до этого кода:

//C# code:
void Foo(ClassA input){
    input = input + 3;
}
void Bar(ClassA ref input){
    input = input + 3;
}
//equivalent C++ code:
void Foo(ClassA&  input){
    input = input + 3;
}
void Bar(ClassA*&  input){
    *input = *input + 3;
}
//equivalent pure C code:
void Fun(ClassA* input){
    *input = *input + 3;
}
void Fun(ClassA** input){
    *(*input) = *(*input) + 3;
}

это приблизительный эквивалент, но это несколько верно.

Ответ 7

Я знаю, что это старый вопрос, но ни один из ответов не дает хорошей прямой причины, по моему мнению.

Вам не нужно использовать ref в этом случае, и вот почему. Рассмотрим эту функцию:

void Foo(MyClass a1, ref MyClass a2, out MyClass b1, int c1, MyStruct d1, ref MyStruct d2)
{
}

Теперь вызовите эту функцию как

MyClass  a = new MyClass();
MyClass  b = null
int      c = 3;
MyStruct d = new MyStruct();

Foo(a, ref a, b, c, d, ref d);

Здесь вы получаете внутри функции:

void Foo(MyClass a1, ref MyClass a2, 
         out MyClass b1, 
         int c1, 
         MyStruct d1, ref MyStruct d2)
{
   a1 is a copy in memory of the pointer to the instantiated class a;
   a2 is the pointer to the instantiated class a;

   b1 is the pointer to b, but has the additional check of having to be set within this function - and cannot be used before being set;

   c1 is a copy in memory of the variable c;

   d1 is a copy in memory of the struct d;
   d2 is the struct d;
}

Важные вещи для реализации:

  • a1 null будет не установить a в null.
  • настройка a2 - null установит a в null.
  • b1 требуется.
  • c1 будет не изменяться c.
  • d1 будет не изменяться d.
  • d2 изменится d.

Это позволяет сделать некоторые странности следующим образом:

void Foo(MyClass x, ref MyClass y)
{
    x = null;
    y.Bar("hi");
}

Вызывается как:

MyClass a = new MyClass();
Foo(a, ref a);

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

Статья Джона Скита вам не поможет, потому что его пример с IntHolder является struct не a class. struct - тип значения типа int и должен обрабатываться одинаково.