Почему синтаксис C# обрабатывает ptr = null иначе, чем разыменование указателя в C++?

В C++ мы можем использовать ptr = nullptr для изменения самого указателя и *ptr для изменения объекта, на который ссылается указатель. Например:

int* ptr = new int(5);

*ptr = 10;  // Modifies the object
ptr = nullptr;  // Changes the pointer itself

В C# есть похожие сценарии со ссылками. Например:

StringBuilder sb = new StringBuilder("Hello");

sb.Append(" World");  // Modifies the object
sb = null;  // Sets the reference to null

Мой вопрос касается синтаксиса и того, почему он ведет себя по-другому. Конкретно:

  • Когда я использую sb.Append(), он работает с объектом, на который указывает sb, а не с самой ссылкой.

  • Когда я использую sb = null, он устанавливает ссылку на null, но не влияет на объект.

Почему синтаксис sb = null в C# не интерпретируется как присвоение объекту значения null, аналогично тому, как ptr = nullptr работает в C++? Или, другими словами, почему бы мне не разыменовать sb, чтобы вызвать метод Append()? Почему в C# нет необходимости явно разыменовывать ссылку для выполнения операций над объектом и как он узнает, когда разыменовывать указатель, а когда манипулировать им напрямую?

Я читал об этом, но не нашел хорошего объяснения. Все, что я узнал до сих пор, это то, что C#, вероятно, не позволяет нам явно разыменовывать, но разве это не очень озадачивает? Концептуально я мог бы знать, что sb = null не может установить указатель на null, но разве синтаксис не говорит об обратном или, по крайней мере, не является однозначным?

Я хочу прояснить, почему синтаксис C# не соответствует напрямую манипулированию указателями в C++ и как язык обеспечивает это различие.

Мой вопрос касается синтаксиса и того, почему он ведет себя по-другому. Потому что C# не имеет ничего общего с C++. Это два разных компьютерных языка. Тот факт, что один язык похож на другой, не означает, что вы можете понять один язык, используя идиомы и практики другого.

PaulMcKenzie 20.08.2024 12:30

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

MakePeaceGreatAgain 20.08.2024 12:32

возможный дубликат Разница между указателем в C++ и ссылочным типом в C#

GSerg 20.08.2024 12:42

Это похоже на сравнение яблок с апельсинами. В C++ тоже есть ссылки, поэтому с помощью StringBuilder& sb вы можете написать sb.Append(" World");. (Однако, в отличие от указателей, ссылка C++ не допускает значения NULL, и вы не используете new).

BoP 20.08.2024 12:42

Я не понимаю разницы, которую вы видите. В обоих случаях xyz = null / nullptr; оставляет объект без изменений.

463035818_is_not_an_ai 20.08.2024 12:44

«аналогично тому, как ptr = nullptr работает в C++?»

463035818_is_not_an_ai 20.08.2024 12:45

хотя я согласен с другими. Непонятно, почему вы ожидаете, что два разных языка будут одинаковыми. «C# не соответствует напрямую манипулированию указателями в C++», с чего бы это?

463035818_is_not_an_ai 20.08.2024 12:46

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

463035818_is_not_an_ai 20.08.2024 12:48

@ 463035818_is_not_an_ai типы значений и ссылочные типы здесь также не применимы, поскольку это подразумевает, что один из них является указателем, а другой — объектом.

MakePeaceGreatAgain 20.08.2024 12:52

@MakePeaceGreatAgain, почему это не применимо? В C++ есть семантика значений, а в C#, похоже, есть ссылочная семантика. Я говорил не о «типах значений и ссылочных типах», а о семантике значений и ссылок в целом, что, я считаю, поможет ОП понять разницу между языками, когда они прочитают об этом.

463035818_is_not_an_ai 20.08.2024 12:56

@ 463035818_is_not_an_ai именно, речь идет о семантике, а не синтаксисе. На семантическом уровне ни типы значений, ни ссылочные типы не нуждаются в арифметике указателей. И, как вы сказали: в C++ также есть типы значений (структуры), хотя в нем есть понятие указателей. Таким образом, семантика значений и ссылок существует и в C++.

MakePeaceGreatAgain 20.08.2024 12:59

@MakePeaceGreatAgain очень извините, но я не понимаю, как все, что вы пишете, связано с моим комментарием о семантике val и ref.

463035818_is_not_an_ai 20.08.2024 13:01

На уровне памяти C# и C++ ведут себя очень по-разному (и в других областях тоже), даже new не делает одно и то же. Объекты имеют разные жизненные циклы, поэтому ваш вопрос в каком-то смысле не имеет смысла. (явное управление памятью или сбор мусора, значение или ссылочная семантика).

Pepijn Kramer 20.08.2024 13:08

Спасибо за все ваши комментарии, они очень полезны. Я хотел бы отметить одну вещь: я не ожидаю, что эти два языка будут вести себя одинаково. Я просто пытаюсь описать, что именно меня смущает: откуда он знает, что sb = null устанавливает ссылку на ноль, а не пытается установить нуль для объекта? Например, почему он не выполняет автоматическое разыменование (или не пытается разыменовать и не выдает ошибку или что-то в этом роде) при sb = null. Откуда он знает, что для ссылки установлено значение null, а не для объекта, на который ссылается?

aim 20.08.2024 13:09

Кроме того, простите мое невежество: у меня сложилось впечатление, что ссылки и указатели — это одно и то же. Теперь я понимаю, что это не так, но первоначальный вопрос все еще остается: как он узнает, когда следует разыменовывать, а когда нет? Я хочу понять синтаксис.

aim 20.08.2024 13:11

@aim в C++ указатель — это тип, и операции с переменной типа указателя будут влиять только на указатель, одна из операций — это оператор разыменования, который при применении к указателю возвращает ссылку на объект другого типа (ссылочный тип), и любая модификация ссылочного объекта изменяет реальный объект.... вам нужно понимать, что это два разных типа, и операции с переменной зависят от ее типа.

Ahmed AEK 20.08.2024 13:25

Например, x += 1 зависит от типа x, если x является типом указателя int* указатель увеличивается, но если x является ссылочным типом int& тогда увеличивается базовый объект, на который ссылается, вы можете преобразовать указатель и ссылку с помощью этих три оператора * and & and -> (последний похож на (*x)., но в нём две буквы вместо 4)

Ahmed AEK 20.08.2024 13:34

О, это многое объясняет, я не могу поверить, что никогда не знал, что указатели и ссылки — это разные вещи. Извините за излишество: но откуда C# узнает, что sb = null пытается установить ссылку на ноль, а не объект, на который ссылается? (Если sb является ссылкой, а sb.Append() изменяет объект, на который ссылается, не должен ли sb = null также попытаться установить для ссылочного объекта значение null?)

aim 20.08.2024 13:35

@aim в C++ у вас может быть указатель на указатель... или ссылка на указатель и т. д. .... в C# у вас есть ссылка и значения, вот и все, это не имеет ничего общего с указателем или ссылкой C++, это это просто имя..... присвоение ссылочным типам изменяет ссылку, тогда как присвоение типам значений изменяет значения, и у вас есть странная ссылка на тип значения ( ref ), которая имеет ту же симантику, что и тип значения, за исключением того, что они готовы на объекте, на который ссылаются (аналогично ссылкам в С++)

Ahmed AEK 20.08.2024 13:48

Это только для оператора присваивания, не пытайтесь вывести поведение оператора из другого языка. В C# оператор точки вызывает функцию для объекта, на который ссылается, это отличается от присваивания, которое изменяет ссылку, а не объект. .. оба оператора изменяют объект в C++, но не в C#, не переводите операторы с одного языка на другой.

Ahmed AEK 20.08.2024 13:53

Попытка соотнести идиомы C# с идиомами C++ покажет только то, что языки различны, а конструкции в языках мало соответствуют друг другу — лишь некоторые сходства и специфичный для языка жаргон, используемый с разной семантикой. (Слишком сложно говорить об обоих языках, используя жаргон каждого языка.)

Eljay 20.08.2024 13:55

Поэтому каждый раз, когда я пытаюсь использовать оператор « = ", он изменяет ссылку, а когда я использую оператор «.». оператор, он изменяет объект, на который ссылается. Это правильно? P.S всем спасибо за помощь, действительно многое прояснилось по ходу дела.

aim 20.08.2024 14:03

@aim для ссылочных типов в C# да, для типов значений и ссылок на типы значений нет... опять же, это зависит от типа объекта

Ahmed AEK 20.08.2024 14:05

Например, @aim int в C# — это тип значения, присвоение ему изменяет объект, тогда как String — это ссылочный тип, присвоение ему изменяет ссылку.

Ahmed AEK 20.08.2024 14:14

Спасибо большое, проблема решена. Не могли бы вы также объяснить, как работает ссылка на типы значений? Был бы очень благодарен.

aim 20.08.2024 14:22

Я думаю, что msdn — лучший источник Learn.microsoft.com/en-us/dotnet/csharp/language-reference/… но, короче говоря, поскольку типы значений передаются по значению, то есть: путем копирования, ключевое слово ref позволяет вам создать.... ссылку на значение.... чтобы при изменении ссылки (путем присвоения) изменялось значение, на которое ссылается.... потому что именно так определяется этот тип... очень полезно при создании out параметр или изменение элемента в списке, это просто псевдоним, другой способ изменения исходного объекта, во многом похожий на ссылки в C++.

Ahmed AEK 20.08.2024 14:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
26
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

На самом деле синтаксис не так уж сильно отличается от C++. Концептуально переменные ссылочного типа в C# и указатели в C++ имеют общую основу, хотя существует немало различий в деталях. Но поскольку вы спросили о синтаксисе, дело обстоит проще:

В C++ вы бы написали:

StringBuilder* sb = new StringBuilder();
(*sb).Append(...);
sb = nullptr; // well, rather delete first, but we don't care here.

(*sb).Append() обычно сокращается до sb->Append(). И вот мы уже очень близки к C#, потому что можно просто подумать, что в C# оператор -> тоже просто записывается как .. Просто компилятор достаточно «умен», чтобы знать, имеет ли оператор доступа к членам ссылочный тип или тип значения в левой части.

Большое спасибо за ваш ответ. Однако есть небольшие вопросы: можем ли мы сказать, что оператор «=» изменяет ссылку, но для «.» оператор, компилятор определяет, предназначен ли последующий метод для ссылки или для объекта, на который ссылается, и затем запускается соответственно?

aim 20.08.2024 14:21

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

PMF 20.08.2024 14:29

Другие вопросы по теме