В чем разница между С# Reference и указателем?

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

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

Я просто не понимаю, что так сложно в отношении указателя? Это в основном просто ссылка на место в памяти, не так ли? Он может вернуть его местоположение и напрямую взаимодействовать с объектом в этом месте?

Я пропустил массивную точку?

Ответ 1

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

EDIT: Концептуально, да. Они более или менее одинаковы.

Ответ 2

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

Возьмем, например, следующий код

int* p1 = GetAPointer();

Это безопасный тип в том смысле, что GetAPointer должен возвращать тип, совместимый с int *. Тем не менее, по-прежнему нет гарантии, что * p1 фактически укажет на int. Это может быть char, двойной или просто указатель на случайную память.

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

string str = GetAString();

В этом случае str имеет одно из двух состояний: 1) оно указывает на отсутствие объекта и, следовательно, равно null или 2) указывает на действительную строку. Это. CLR гарантирует, что это так. Он не может и не будет для указателя.

Ответ 3

Ссылка является "абстрактным" указателем: вы не можете выполнять арифметику со ссылкой, и вы не можете играть с низкоуровневыми трюками со своим значением.

Ответ 4

Сначала я думаю, вам нужно определить "указатель" в вашей семантике. Вы имеете в виду указатель, который вы можете создать в небезопасном коде с fixed? Вы имеете в виду IntPtr, который вы получаете от, возможно, родного вызова или Marshal.AllocHGlobal? Вы имеете в виду GCHandle? Все это по сути то же самое - представление адреса памяти, в котором что-то хранится, будь то класс, число, структура, что угодно. И для записи они, безусловно, могут быть в куче.

Указатель (все вышеперечисленные версии) является фиксированным элементом. ГК не знает, что находится по этому адресу, и поэтому не имеет возможности управлять памятью или жизнью объекта. Это означает, что вы теряете все преимущества системы сбора мусора. Вы должны вручную управлять памятью объекта, и у вас есть потенциал для утечек.

Ссылка, с другой стороны, в значительной степени является "управляемым указателем", о котором знает GC. Он по-прежнему является адресом объекта, но теперь GC знает детали цели, поэтому он может перемещать его, делать компиляции, завершать, удалять и все другие приятные вещи, которые управляемая среда делает.

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

Изменить: Фактически вот хороший пример, когда вы можете использовать "указатель" в управляемом коде - в этом случае это GCHandle, но то же самое можно было бы сделать с AllocHGlobal или с помощью фиксированного байтового массива или структуры. Я предпочитаю GCHandle, потому что мне больше нравится ".NET".

Ответ 5

Указатели указывают на местоположение в адресном пространстве памяти. Ссылки указывают на структуру данных. Структуры данных все перемещались все время (ну, не так часто, но время от времени) сборщиком мусора (для уплотнения пространства памяти). Кроме того, как вы сказали, структуры данных без ссылок будут получать мусор через некоторое время.

Кроме того, указатели могут использоваться только в небезопасном контексте.

Ответ 6

Я думаю, что для разработчиков важно понять концепцию указателя, то есть понять косвенность. Это не означает, что они обязательно должны использовать указатели. Также важно понять, что понятие ссылки отличается от концепции указателя, хотя и тонко, но что реализация ссылки почти всегда равна указатель.

То есть переменная, содержащая ссылку, представляет собой блок памяти размером в указатель, содержащий указатель на объект. Однако эту переменную нельзя использовать так же, как можно использовать переменную указателя. В С# (и C и С++,...) указатель может быть проиндексирован как массив, но ссылка не может. В С# ссылка отслеживается сборщиком мусора, указатель не может быть. В С++ указатель можно переназначить, ссылка не может. Синтаксически и семантически, указатели и ссылки совершенно разные, но механически они одинаковы.

Ответ 7

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

Ответ 8

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

Обратите внимание: каждая ссылка на объект фактически инкапсулирует два вида информации: (1) содержимое поля идентифицируемого объекта и (2) множество других ссылок на один и тот же объект. Хотя нет механизма, с помощью которого система может быстро идентифицировать все ссылки, которые существуют для объекта, множество других ссылок, которые существуют для объекта, часто может быть наиболее важной инкапсулированной ссылкой (это особенно верно, когда вещи типа Object используются как вещи, например, токены блокировки). Хотя система хранит несколько бит данных для каждого объекта для использования в GetHashCode, объекты не имеют реальной идентичности за пределами набора ссылок, которые существуют для них. Если X содержит единственную постоянную ссылку на объект, замена X ссылкой на новый объект с тем же содержимым поля не будет иметь идентифицируемого эффекта, кроме как изменить биты, возвращаемые GetHashCode(), и даже этот эффект isn Гарантировано.

Ответ 9

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

Ответ 10

Одно из самых больших преимуществ ссылок на указатели - большая простота и удобочитаемость. Как всегда, когда вы упрощаете что-то, вы упрощаете его использование, но за счет гибкости и контроля вы получаете материал низкого уровня (как говорили другие люди).

Указатели часто критикуются за то, что они "уродливые".

class* myClass = new class();

Теперь каждый раз, когда вы его используете, вы должны сначала разыменовать его либо

myClass->Method() or (*myClass).Method()

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

Для меня именно поэтому ссылки были "рождены" в первую очередь, чтобы обеспечить ту же выгоду, что и указатели, но без всего синтаксиса указателя. Теперь вы можете передать фактический объект (а не только его значение). И у вас есть более читаемый, нормальный способ взаимодействия с объектом.

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

Ссылки на С++ отличаются от ссылок на С#/Java тем, что после того, как вы назначили ему значение, вы не могли повторно назначить его (и его нужно назначить, когда он был объявлен). Это было то же самое, что использовать указатель const (указатель, который нельзя было переназначить другому объекту).

Java и С# - очень высокий уровень, современные языки, которые очищали множество беспорядков, накопившихся на C/С++ на протяжении многих лет, и указатели были определенно одной из тех вещей, которые нужно было "очистить".

Насколько ваш комментарий о знании указателей делает вас более сильным программистом, это верно в большинстве случаев. Если вы знаете "как" что-то работает, а не просто использовать его, не зная, я бы сказал, что это часто может дать вам преимущество. Какая часть края всегда будет изменяться. В конце концов, использование чего-то, не зная, как оно реализовано, является одной из многих красот ООП и интерфейсов.

В этом конкретном примере, что знание о указателях поможет вам со ссылками? Понимая, что ссылка на С# НЕ является самим объектом, но указывает на объект, это очень важная концепция.

# 1: вы НЕ передаете значение Ну для начала, когда вы используете указатель, вы знаете, что указатель содержит только адрес, что он. Сама переменная почти пуста и почему ее так приятно передавать в качестве аргументов. В дополнение к увеличению производительности вы работаете с фактическим объектом, поэтому любые сделанные вами изменения не являются временными

# 2: полиморфизм/интерфейсы Когда у вас есть ссылка, являющаяся типом интерфейса и указывающая на объект, вы можете вызывать только методы этого интерфейса, даже если объект может иметь гораздо больше возможностей. Объекты могут также реализовывать одни и те же методы по-разному.

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

Я не думаю, что всем нужно начинать с указателей, но важно то, что они понимают, почему используются ссылки вместо типов значений, и лучший способ понять это - смотреть на своего предка, указатель.