использование ref с классом C#

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

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

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

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

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

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

7 ответов


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


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

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

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

параметры:

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

справочные параметры:

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

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


единственный раз, когда вам нужно использовать 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");
}

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

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
}

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

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

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

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

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

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


я добавляю этот ответ для программистов, которые привыкли к C++, как я.

классы, интерфейсы, delegatess и массивы 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;
}

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


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

вам не нужно использовать 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;
}

важные вещи, чтобы понять:

  1. задание a1 to null будет не установить a to null.
  2. задание a2 to null устанавливается a to null.
  3. задание b1 не требуется.
  4. задание c1 будет не изменить c.
  5. задание d1 будет не изменить d.
  6. задание 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 не class. Struct это типа значения типа int и должен обрабатываться таким же образом.