Один парень задал мне этот вопрос пару месяцев назад, и я не мог его подробно объяснить. В чем разница между ссылочным типом и типом значения в C #?
Я знаю , что типы значений int
, bool
, float
и т.д. , и ссылочные типы delegate
, interface
и т.д. Или это не так, тоже?
Вы можете мне профессионально объяснить это?
c#
.net
value-type
reference-type
тугберк
источник
источник
Ответы:
Ваши примеры немного странно , потому что в то время
int
,bool
иfloat
определенные типов, интерфейсы и делегаты виды типа - так же , какstruct
иenum
являются видами типов значений.Я написал письмо на объяснение ссылочных типов и типов значений в этой статье . Я был бы рад дополнить все, что вас смущает.
Версия "TL; DR" - это думать о том, каково значение переменной / выражения определенного типа. Для типа значения значение - это сама информация. Для ссылочного типа значение является ссылкой, которая может иметь значение NULL или может быть способом перехода к объекту, содержащему информацию.
Например, представьте переменную как лист бумаги. На нем могло быть написано значение «5» или «ложь», но на нем не мог быть мой дом ... он должен был иметь направление к моему дому. Эти направления являются эквивалентом ссылки. В частности, у двух человек могут быть разные листы бумаги с одинаковыми указаниями к моему дому - и если один человек будет следовать этим указаниям и покрасить мой дом в красный цвет, то второй человек тоже увидит это изменение. Если бы у них обоих были отдельные фотографии моего дома на бумаге, то один человек, раскрашивающий их бумагу, вообще не изменил бы бумагу другого человека.
источник
while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types
. Что вы имеете в виду под int, bool - конкретными типами? Все в C #, например int, bool, float, class, interface, delegate, является типом (точнее, типом данных). Типы данных в C # разделены на «ссылочный тип» и «тип значения». Тогда почему вы говорите, что int - это особый тип, а интерфейс - это разновидность типа?int
является структурой,string
является классом,Action
является делегатом и т. д. Ваш список «int, bool, float, class, interface, delegate» - это список, содержащий разные типы вещей, точно так же, как «10, int» список, содержащий разные вещи.Тип значения:
Содержит какое-то значение, а не адреса памяти
Пример:
Struct
Место хранения:
TL; DR : значение переменной сохраняется везде, где оно отменено. Например, локальные переменные находятся в стеке, но когда они объявлены внутри класса в качестве члена, они находятся в куче, тесно связанном с классом, в котором они объявлены.
Дольше : Таким образом, типы значений сохраняются везде, где они объявлены. Например: значение an
int
внутри функции как локальная переменная будет храниться в стеке, в то время как значение inint
, объявленное как член в классе, будет сохранено в куче с классом, в котором оно объявлено. Тип значения на Тип жизни класса точно такой же, как у класса, в котором он объявлен, и почти не требует работы сборщика мусора. Хотя это более сложно, я бы сослался на книгу @JonSkeet " C # In Depth "Память в .NET »для более краткого пояснения.Преимущества:
Тип значения не требует дополнительной сборки мусора. Он собирает мусор вместе с экземпляром, в котором он живет. Локальные переменные в методах очищаются после выхода из метода.
Недостатки:
Когда в метод передается большой набор значений, принимающая переменная фактически копирует, поэтому в памяти остаются два избыточных значения.
Поскольку классы пропускаются. Он теряет все преимущества
Тип ссылки:
Содержит адрес памяти значения, а не значения
Пример:
Класс
Место хранения:
Хранится в куче
Преимущества:
Когда вы передаете ссылочную переменную методу, и она меняет, она действительно изменяет исходное значение, тогда как в типах значений берется копия данной переменной, и это значение изменяется.
Когда размер переменной больше, подходит ссылочный тип
Поскольку классы представляют собой переменные ссылочного типа, они обеспечивают возможность повторного использования, что способствует объектно-ориентированному программированию.
Недостатки:
Больше ссылок на работу при выделении и разыменовании при чтении значения. Дополнительная перегрузка для сборщика мусора
источник
Мне было легче понять разницу между ними, если вы знаете, как компьютер распределяет данные в памяти, и знаете, что такое указатель.
Ссылка обычно связана с указателем. Это означает, что адрес памяти, в котором находится ваша переменная, фактически содержит другой адрес памяти фактического объекта в другом месте памяти.
Пример, который я собираюсь дать, сильно упрощен, так что относитесь к нему с недоверием.
Представьте себе компьютерную память - это набор расположенных в ряд абонентских ящиков (начиная с абонентского ящика 0001 и заканчивая абонентским ящиком n), которые могут содержать что-то внутри. Если почтовые ящики не делают этого за вас, попробуйте хеш-таблицу, словарь, массив или что-то подобное.
Таким образом, когда вы делаете что-то вроде:
var a = "Привет";
компьютер сделает следующее:
Так что это что-то вроде псевдонима (0500 - это).
Тип значения будет содержать фактическую вещь в своей ячейке памяти.
Таким образом, когда вы делаете что-то вроде:
var a = 1;
компьютер сделает следующее:
Обратите внимание, что мы не выделяем дополнительную память для хранения фактического значения (1). Таким образом, фактически хранится фактическое значение , поэтому он называется типом значения.
источник
Это из моего сообщения с другого форума около двух лет назад. Хотя языком является vb.net (в отличие от C #), концепции типа значения и ссылочного типа единообразны во всем .net, и примеры все еще актуальны.
Также важно помнить, что в .net ВСЕ типы технически являются производными от базового типа Object. Типы значений предназначены для того, чтобы вести себя как таковые, но, в конце концов, они также наследуют функциональность объекта базового типа.
A. Типы значений - это просто: они представляют собой отдельную область в памяти, где хранится дискретное ЗНАЧЕНИЕ. Типы значений имеют фиксированный размер памяти и хранятся в стеке, который представляет собой набор адресов фиксированного размера.
Когда вы делаете подобное заявление:
Вы сделали следующее:
Значение каждой переменной существует отдельно в каждой ячейке памяти.
Б. Типы ссылок могут быть разных размеров. Следовательно, они не могут храниться в «стеке» (помните, стек представляет собой набор выделений памяти фиксированного размера?). Они хранятся в «управляемой куче». Указатели (или «ссылки») на каждый элемент в управляемой куче сохраняются в стеке (как адрес). Ваш код использует эти указатели в стеке для доступа к объектам, хранящимся в управляемой куче. Поэтому, когда ваш код использует ссылочную переменную, он фактически использует указатель (или «адрес» области памяти в управляемой куче).
Допустим, вы создали класс с именем clsPerson со строкой Property Person.Name.
В этом случае, когда вы делаете такое заявление:
В приведенном выше случае свойство p1.Name вернет «Джим Моррисон», как и следовало ожидать. Свойство p2.Name ТАКЖЕ вернет «Джим Моррисон», как и следовало ожидать. Я считаю, что и p1, и p2 представляют разные адреса в стеке. Однако теперь, когда вы присвоили p2 значение p1, как p1, так и p2 указывают на ОДИН МЕСТО в управляемой куче.
Теперь рассмотрите эту ситуацию:
В этой ситуации вы создали один новый экземпляр класса person в управляемой куче с указателем p1 в стеке, который ссылается на объект, и снова присвоили свойству Name экземпляра объекта значение «Джим Моррисон». Затем вы создали еще один указатель p2 в стеке и указали его на тот же адрес в управляемой куче, что и на адрес p1 (когда вы сделали присвоение p2 = p1).
А вот и поворот. Когда вы присваиваете свойству Name p2 значение «Janis Joplin», вы меняете свойство Name для объекта, на который ссылаются как p1, так и p2, так что если вы выполнили следующий код:
Это имело смысл?
Последний. Если вы сделаете ЭТО:
Теперь у вас есть два разных объекта Person. Однако, как только вы сделаете ЭТО снова:
Вы снова указали на «Джима Моррисона». (Я не совсем уверен, что случилось с объектом в куче, на который ссылается p2 ... Я ДУМАЮ, что теперь он вышел за рамки. Это одна из тех областей, где, надеюсь, кто-то может меня поправить ...). -EDIT: Я ВЕРЮ, поэтому вы должны установить p2 = Nothing ИЛИ p2 = New clsPerson перед выполнением нового назначения.
Еще раз, если вы сейчас сделаете ЭТО:
Оба msgBoxes теперь будут возвращать "Jimi Hendrix"
Это может немного сбивать с толку, и я скажу в последний раз, что некоторые детали могут быть неверными.
Удачи, и, надеюсь, другие, кто знает лучше меня, придут, чтобы помочь прояснить некоторые из этих вопросов. . .
источник
тип данных значения и ссылочный тип данных
1) значение (содержит данные напрямую), но ссылка (относится к данным)
2) по значению (каждая переменная имеет свою копию), но
по ссылке (более чем переменная может ссылаться на некоторые объекты)
3) по значению (операционная переменная не может влиять на другую переменную), но по ссылке (переменная может влиять на другие)
4) типы значений (int, bool, float), но ссылочные типы (массив, объекты класса, строка)
источник
Тип значения:
Фиксированный размер памяти.
Хранится в памяти стека.
Имеет фактическое значение.
Ex. int, char, bool и т. д.
Тип ссылки:
Нефиксированная память.
Хранится в памяти кучи.
Содержит адрес памяти фактического значения.
Ex. строка, массив, класс и т. д.
источник
«Переменные, основанные на типах значений, непосредственно содержат значения. Присвоение переменной одного типа значения другому копирует содержащееся значение. Это отличается от назначения переменных ссылочного типа, которые копируют ссылку на объект, но не на сам объект». из библиотеки Microsoft.
Вы можете найти более полный ответ здесь и здесь .
источник
Иногда объяснения не помогают, особенно новичкам. Вы можете представить себе тип значения как файл данных и ссылочный тип как ярлык для файла.
Поэтому, если вы копируете ссылочную переменную, вы копируете только ссылку / указатель на реальные данные где-нибудь в памяти. Если вы копируете тип значения, вы действительно клонируете данные в памяти.
источник
С эзотерической точки зрения это, вероятно, неверно, но для простоты:
Типы значений - это значения, которые обычно передаются «по значению» (таким образом, копируя их). Ссылочные типы передаются «по ссылке» (таким образом, давая указатель на исходное значение). Стандарт .NET ECMA не дает никаких гарантий относительно того, где хранятся эти «вещи». Вы можете создать реализацию .NET без стека или без кучи (второй вариант будет очень сложным, но вы, вероятно, сможете, используя волокна и множество стеков)
Структуры относятся к типу значений (int, bool ... являются структурами или, по крайней мере, моделируются как ...), классы имеют ссылочный тип.
Типы значений происходят от System.ValueType. Тип ссылки происходит от System.Object.
Теперь ... В конце концов, у вас есть Тип значения, "ссылочные объекты" и ссылки (в C ++ они будут называться указателями на объекты. В .NET они непрозрачны. Мы не знаем, что они собой представляют. С нашей точки зрения, они являются «ручками» объекта). Эти длины похожи на типы значений (они передаются по копии). Таким образом, объект состоит из объекта (ссылочного типа) и нуля или более ссылок на него (которые аналогичны типам значений). Когда есть нулевые ссылки, GC, вероятно, соберет их.
Как правило (в реализации .NET по умолчанию) тип значения может находиться в стеке (если они являются локальными полями) или в куче (если они являются полями класса, если они являются переменными в функции итератора, если они являются переменными, на которые ссылается замыкание, если они являются переменными в асинхронной функции (с использованием более нового Async CTP) ...). Ссылочное значение может попасть только в кучу. Ссылки используют те же правила, что и типы значений.
В случаях типа значения, которые помещаются в кучу, потому что они находятся в функции итератора, асинхронной функции или на которые ссылается закрытие, если вы посмотрите скомпилированный файл, вы увидите, что компилятор создал класс для размещения этих переменных , и класс создается при вызове функции.
Я не умею писать длинные вещи, и у меня есть дела поважнее в моей жизни. Если вам нужна "точная" "академическая" "правильная" версия, прочтите ЭТО:
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
Уже 15 минут ищу! Это лучше, чем версии msdn, потому что это сжатая «готовая к использованию» статья.
источник
Самый простой способ думать о ссылочных типах - рассматривать их как «идентификаторы объектов»; единственное, что можно делать с идентификатором объекта, - это создавать его, копировать, запрашивать или изменять тип объекта или сравнивать два на равенство. Попытка сделать что-либо еще с идентификатором объекта будет рассматриваться как сокращение для выполнения указанного действия с объектом, на который ссылается этот идентификатор.
Предположим, у меня есть две переменные X и Y типа Car - ссылочный тип. Y имеет «ID объекта № 19531». Если я скажу «X = Y», это приведет к тому, что X будет содержать «ID объекта № 19531». Обратите внимание, что ни у X, ни у Y нет машины. Автомобиль, также известный как «ID объекта № 19531», хранится в другом месте. Когда я скопировал Y в X, все, что я сделал, это скопировал идентификационный номер. Теперь предположим, что я сказал X.Color = Colors.Blue. Такое заявление будет рассматриваться как инструкция найти «объект ID # 19531» и покрасить его в синий цвет. Обратите внимание, что хотя X и Y теперь относятся к синему автомобилю, а не к желтому, это утверждение фактически не влияет на X или Y, потому что оба по-прежнему относятся к «объекту с идентификатором № 19531», который по-прежнему остается тем же автомобилем, что и он. всегда было.
источник
Типы переменных и ссылочное значение легко применять и хорошо применять к модели предметной области, что облегчает процесс разработки.
Чтобы развеять миф о количестве «типов значений», я прокомментирую, как это обрабатывается на платформе. NET, в частности, в C # (CSharp) при вызове APIS и отправке параметров по значению, по ссылке, в наших методах и функциях, а также о том, как правильно обрабатывать проходы этих значений.
Прочтите эту статью Значение и справочник по типу переменной в C #
источник
Предположим
v
, это выражение / переменная типа значения и выражение / переменнаяr
ссылочного типаИтак, переменная типа значения хранит фактическое значение (5 или "h"). Переменная ссылочного типа хранит только ссылку на метафорический блок, в котором находится значение.
источник
Прежде чем объяснять различные типы данных, доступные в C #, важно упомянуть, что C # - это строго типизированный язык. Это означает, что каждая переменная, константа, входной параметр, тип возвращаемого значения и вообще каждое выражение, оценивающее значение, имеет тип.
Каждый тип содержит информацию, которая будет встроена компилятором в исполняемый файл в виде метаданных, которые будут использоваться средой CLR для обеспечения безопасности типов при выделении и освобождении памяти.
Если вы хотите узнать, сколько памяти выделяет конкретный тип, вы можете использовать оператор sizeof следующим образом:
Вывод покажет количество байтов, выделенных каждой переменной.
Информация, относящаяся к каждому типу:
Члены (методы, поля, события и т. Д.), Содержащиеся в типе. Например, если мы проверим определение типа int, мы найдем следующую структуру и члены:
Управление памятью Когда в операционной системе запущено несколько процессов и объема оперативной памяти недостаточно, чтобы вместить все это, операционная система сопоставляет части жесткого диска с оперативной памятью и начинает хранить данные на жестком диске. Операционная система будет использовать специальные таблицы, в которых виртуальные адреса сопоставлены с соответствующими физическими адресами для выполнения запроса. Эта возможность управления памятью называется виртуальной памятью.
В каждом процессе доступная виртуальная память организована в следующие 6 разделов, но для актуальности этой темы мы сосредоточимся только на стеке и куче.
Стек Стек представляет собой структуру данных LIFO (последний пришел - первым ушел), размер которой зависит от операционной системы (по умолчанию для компьютеров ARM, x86 и x64 резерв Windows составляет 1 МБ, а в Linux - от 2 МБ до 8 МБ в зависимости от версия).
Этот раздел памяти автоматически управляется ЦП. Каждый раз, когда функция объявляет новую переменную, компилятор выделяет новый блок памяти размером с его размер в стеке, а когда функция завершается, блок памяти для переменной освобождается.
Куча. Эта область памяти не управляется ЦП автоматически, и ее размер больше, чем размер стека. Когда вызывается ключевое слово new, компилятор начинает поиск первого свободного блока памяти, который соответствует размеру запроса. и когда он его находит, он помечается как зарезервированный с помощью встроенной функции C malloc () и возвращает указатель на это место. Также возможно освободить блок памяти с помощью встроенной функции C free (). Этот механизм вызывает фрагментацию памяти и должен использовать указатели для доступа к правому блоку памяти, он медленнее, чем стек для выполнения операций чтения / записи.
Пользовательские и встроенные типы. Хотя C # предоставляет стандартный набор встроенных типов, представляющих целые числа, логические значения, текстовые символы и т. Д., Вы можете использовать такие конструкции, как struct, class, interface и enum, для создания ваших собственных типов.
Пример настраиваемого типа с использованием конструкции struct:
Типы значений и ссылочные типы Мы можем разделить тип C # на следующие категории:
Типы значений Типы значений являются производными от класса System.ValueType, а переменные этого типа содержат свои значения в выделенной им памяти в стеке. Две категории типов значений - это структура и перечисление.
В следующем примере показан член типа boolean. Как видите, явной ссылки на класс System.ValueType нет, это происходит потому, что этот класс наследуется структурой.
Ссылочные типы С другой стороны, ссылочные типы не содержат фактических данных, хранящихся в переменной, а содержат адрес кучи, в которой хранится значение. Категории ссылочных типов - это классы, делегаты, массивы и интерфейсы.
Во время выполнения, когда объявляется переменная ссылочного типа, она содержит значение null до тех пор, пока ей не будет присвоен объект, созданный с использованием ключевых слов new.
В следующем примере показаны члены универсального типа List.
Если вы хотите узнать адрес памяти определенного объекта, класс System.Runtime.InteropServices предоставляет способ доступа к управляемым объектам из неуправляемой памяти. В следующем примере мы собираемся использовать статический метод GCHandle.Alloc () для выделения дескриптора строки, а затем метод AddrOfPinnedObject для получения ее адреса.
Выход будет
Ссылки Официальная документация: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019
источник
Существует множество мелких деталей различий между типами значений и ссылочными типами, которые явно указаны в стандарте, и некоторые из них нелегко понять, особенно для начинающих.
См. Стандарт 33 ECMA , Common Language Infrastructure (CLI) . Интерфейс командной строки также стандартизирован ISO. Я бы дал ссылку, но для ECMA мы должны загрузить PDF-файл, и эта ссылка зависит от номера версии. Стандарты ISO стоят денег.
Одно отличие состоит в том, что типы значений могут быть помещены в коробку, а ссылочные типы - нет. Бывают исключения, но они довольно технические.
Типы значений не могут иметь конструкторы экземпляров или финализаторы без параметров, и они не могут ссылаться на себя. Обращение к себе означает, например, что если существует тип значения Node, то член Node не может быть Node . Я думаю, что в спецификациях есть другие требования / ограничения, но если это так, то они не собраны в одном месте.
источник