C# ref это как указатель на C / C++ или ссылка на C++?

я работаю с ref и не понимаю ясно " это как указатель, как в C / C++, или это как ссылка на C++?"

почему я задал такой слабый вопрос, как ты подумал? Потому что, когда я читаю книги C# / .NET, msdn или разговариваю с разработчиками C#, меня смущают следующие причины:

  • разработчики C# предлагают не использовать ref в аргументах функции, e.g. ...(ref Type someObject) не пахнет хорошо для им и предлагают ...(Type someObject), Я действительно не понимаю ясно это предложение. Причин я слышал: лучше работать с копией объекта, а затем использовать его в качестве возвращаемого значения, чтобы не повредить память, ссылки и т. д... Часто я слышу такое объяснение об объектах подключения БД. Как и в моем простом опыте C / C++, я действительно не понимаю, почему использовать ссылку-это плохо в C#? Я контролирую жизнь объекта и его распределение/перераспределение памяти и т. д... Я читаю только в книгах и форумах советует it's bad, because you can corrupt your connection and cause a memory leak by a reference lose, поэтому я контролирую жизнь объекта, я могу вручную контролировать то, что я действительно хочу, так почему это плохо?
  • в настоящее время, читая разные книги и разговаривая с разными людьми, я не понимаю, это ref указатель (*) или ссылка, как в C++ по & ? Как я помню, указатели в C / C++ всегда выделяют пространство размером void* Тип - 4 байта (допустимый размер зависит от архитектуры), где размещается адрес структуры или переменной. В C++ передача ссылки & нет новых выделений из кучи / стека, и вы работаете с уже определенными объектами в пространстве памяти, и нет суб-выделения памяти для указателя извне, как в plain C. Так что же ref в C#? .NET VM обрабатывает его как указатель на простом C / C++ и его GC выделяет временное пространство для указателя или он работает как ссылка в C++? Делает ref работать только с управляемыми типами правильно или для типов значений, таких как bool, int лучше чтобы переключить unsafe код и пройти через указатель в неуправляемом стиле?

5 ответов


В C#, когда вы видите что-то, относящееся к ссылочному типу (то есть типу, объявленному с class вместо struct), то вы по существу всегда имеете дело с объектом через указатель. В C++ все по умолчанию является типом значения, тогда как в C# все по умолчанию является ссылочным типом.

когда вы говорите " ref "в списке параметров C#, то, что вы действительно говорите, больше похоже на "указатель на указатель"."Вы говорите, что в методе, который вы хотите заменить не содержимое объекта, а ссылка на сам объект в коде, вызывающем ваш метод.

если это не ваше намерение, то вы должны просто передать ссылочный тип напрямую; в C# передача ссылочных типов дешева (сродни передаче ссылки в C++).

узнать/понять разницу между типами значений и ссылочными типами в C#. Они являются основной концепцией на этом языке, и все будет очень запутанным, если вы попытаетесь думать, используя объектная модель C++ в C# land.

следующие по существу семантически эквивалентные программы:

#include <iostream>

class AClass
{
    int anInteger;
public:
    AClass(int integer)
        : anInteger(integer)
    {  }

    int GetInteger() const
    {
        return anInteger;
    }

    void SetInteger(int toSet)
    {
        anInteger = toSet;
    }
};

struct StaticFunctions
{
    // C# doesn't have free functions, so I'll do similar in C++
    // Note that in real code you'd use a free function for this.

    static void FunctionTakingAReference(AClass *item)
    {
        item->SetInteger(4);
    }

    static void FunctionTakingAReferenceToAReference(AClass **item)
    {
        *item = new AClass(1729);
    }
};

int main()
{
    AClass* instanceOne = new AClass(6);
    StaticFunctions::FunctionTakingAReference(instanceOne);
    std::cout << instanceOne->GetInteger() << "\n";

    AClass* instanceTwo;
    StaticFunctions::FunctionTakingAReferenceToAReference(&instanceTwo);
    // Note that operator& behaves similar to the C# keyword "ref" at the call site.
    std::cout << instanceTwo->GetInteger() << "\n";

    // (Of course in real C++ you're using std::shared_ptr and std::unique_ptr instead,
    //  right? :) )
    delete instanceOne;
    delete instanceTwo;
}

и для C#:

using System;

internal class AClass
{
    public AClass(int integer)
        : Integer(integer)
    {  }

    int Integer { get; set; }
}

internal static class StaticFunctions
{
    public static void FunctionTakingAReference(AClass item)
    {
        item.Integer = 4;
    }

    public static void FunctionTakingAReferenceToAReference(ref AClass item)
    {
        item = new AClass(1729);
    }
}

public static class Program
{
    public static void main()
    {
        AClass instanceOne = new AClass(6);
        StaticFunctions.FunctionTakingAReference(instanceOne);
        Console.WriteLine(instanceOne.Integer);

        AClass instanceTwo  = new AClass(1234); // C# forces me to assign this before
                                                // it can be passed. Use "out" instead of
                                                // "ref" and that requirement goes away.
        StaticFunctions.FunctionTakingAReferenceToAReference(ref instanceTwo);
        Console.WriteLine(instanceTwo.Integer);
    }
}

A ref в C# эквивалентно ссылке на C++:

  • их целью является pass-by-reference
  • нет нулевых ссылок
  • нет неинициализированных ссылок
  • нельзя перепривязать ссылок
  • когда вы пишете ссылку, вы фактически обозначаете указанную переменную

некоторые код C++:

void foo(int& x)
{
    x = 42;
}
// ...
int answer = 0;
foo(answer);

эквивалентный код на C#:

void foo(ref int x)
{
    x = 42;
}
// ...
int answer = 0;
foo(ref answer);

каждая ссылка в C# является указателем на объекты в куче как указатель в C++ , а ссылка на C# такая же, как & в C++

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

String a = "  A  ";
String b = a.Trim();

в этом случае я уверен, что остается неизменным. В математике следует рассматривать как уступку это визуально говорит о том, что b изменяется здесь с согласия программиста.

a = a.Trim();

этот код изменит сам себя, и кодер знает об этом.

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


Это похоже на ночной кошмар. Если у меня есть объект, для которого зарегистрированы события и передают его в функцию по ссылке, и эта ссылка затем перераспределяется, dispose должен быть вызван или память будет выделена до тех пор, пока программа не будет закрыта. Если метод Dispose вызывается все зарегистрированы на ивенты объекты не будут зарегистрированы и все, что он прописан не будет прописан. Как кто-то может держать это в секрете? Я думаю вы можете сравнить адреса памяти и попытаться вернуть все в норму, если вы не сойдете с ума.


C# не имеет равных значений указателей на C++ и работает со ссылками. ref добавляет уровень косвенности. Он делает аргумент типа значения ссылкой, и при использовании со ссылочным типом он делает его ссылкой на ссылку.

короче говоря, это позволяет переносить любые изменения в тип значения вне вызова метода. Для ссылочного типа он позволяет заменить исходную ссылку на совершенно другой объект (а не просто изменить содержимое объекта). Его можно использовать, если вы хотите повторно инициализировать объект внутри метода, и единственный способ сделать это, чтобы воссоздать его. Хотя я бы постарался избежать такого подхода.

Итак, чтобы ответить на ваш вопрос ref было бы похоже на ссылку C++ на ссылку.

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

вышесказанное верно для безопасного кода. Указатели существуют в небезопасном C# и используются в некоторых очень конкретных случаях.