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

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

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

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

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

Ответ 1

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

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

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

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

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

#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;
}

И для С#:

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);
    }
}

Ответ 2

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

  • Их целью является передача по ссылке
  • Нет нулевых ссылок
  • Нет неинициализированных ссылок
  • Вы не можете восстановить ссылки
  • Когда вы указываете ссылку, вы на самом деле обозначаете указанную переменную

Некоторые С++-коды:

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

Эквивалентный код С#:

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

Ответ 3

Каждая ссылка в С# является указателем на объекты в куче в качестве указателя на С++, а ссылка на С# такая же, как и в С++

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

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

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

a = a.Trim();

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

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

Ответ 4

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

Ответ 5

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

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

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

ИЗМЕНИТЬ

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