C # дает вам «меньше веревки, чтобы повеситься», чем C ++? [закрыто]


14

Джоэл Спольски охарактеризовал C ++ как «достаточно веревки, чтобы повеситься» . На самом деле он резюмировал «Эффективный C ++» Скотта Мейерса:

Это книга, которая в основном гласит: C ++ - это достаточно веревки, чтобы повеситься, а затем пара лишних миль веревки, а затем пара таблеток-самоубийц, которые замаскированы под M & Ms ...

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

Вот мои вопросы:

  1. C # избегает ловушек, которых избегают в C ++ только при тщательном программировании? Если да, то в какой степени и как их избежать?
  2. Есть ли в C # новые подводные камни, о которых должен знать новый программист C #? Если так, то почему их нельзя было избежать с помощью дизайна C #?

10
Из FAQ : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Я считаю, что это квалифицируется как такой вопрос ...
Одед

@Oded Вы имеете в виду вопрос с заголовком с ограничением символов? Или мои 3+ более точные вопросы в теле моего поста?
alx9r

3
Честно говоря - и название, и каждый из «более точных вопросов».
Одед

3
Я начал мета-обсуждение этого вопроса.
Одд

1
Что касается вашего теперь удаленного третьего вопроса, серия « Эффективное C #» Билла Вагнера (теперь 3 книги) научила меня больше о программировании на C #, чем что-либо еще, что я читал на эту тему. Мартинс рассматривает EC # правильно, потому что он никогда не может быть прямой заменой Effective C ++, но он ошибается, полагая, что так и должно быть. Как только вам больше не нужно беспокоиться о легких ошибках, вы должны переходить к более сложным ошибкам.
Марк Бут

Ответы:


33

Принципиальное отличие C ++ от C # заключается в неопределенном поведении .

Это не имеет ничего общего с ручным управлением памятью. В обоих случаях это решенная проблема.

C / C ++:

В C ++, когда вы делаете ошибку, результат не определен.
Или, если вы попытаетесь сделать определенные предположения о системе (например, целочисленное переполнение со знаком), есть вероятность, что ваша программа будет неопределенной.

Возможно, прочитайте эту серию из трех частей о неопределенном поведении.

Это то, что делает C ++ таким быстрым - компилятору не нужно беспокоиться о том, что происходит, когда что-то идет не так, поэтому он может избежать проверки на правильность.

C #, Java и т. Д.

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

Все остальное просто соус.


Все неопределенные вещи действительно определены реализацией, поэтому, если вы переполните целое число без знака в Visual Studio, вы получите исключение, если вы включите правильные флаги компилятора. Теперь я знаю, что вы говорите об этом, но это не неопределенное поведение , просто люди обычно его не проверяют. (то же самое с действительно неопределенным поведением как оператор ++, это хорошо определено каждым компилятором). Вы можете сказать то же самое с C #, только есть только 1 реализация - много «неопределенного поведения», если вы работаете в Mono - например. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb

1
Это действительно определяется или определяется только тем, что делает текущая реализация .net в текущей версии Windows? Даже неопределенное поведение в c ++ полностью определено, если вы определите его как то, что делает g ++.
Мартин Беккет

6
Переполнение целых чисел без знака вовсе не UB. Это перелив подписанных целых чисел , что это UB.
DeadMG

6
@gbjbaanb: Как DeadMG сказал - знаковое целое переполнение является неопределенным. Это не определяется реализацией. Эти фразы имеют определенные значения в стандарте C ++, и это не одно и то же. Не делай эту ошибку.
user541686

1
@CharlesSalvia: Ну, как именно "C ++ облегчает использование кеша процессора", чем C #? И какой контроль С ++ дает вам над памятью, которую вы не можете иметь в С #?
user541686

12

C # избегает ловушек, которых избегают в C ++ только при тщательном программировании? Если да, то в какой степени и как их избежать?

Большинство это делает, некоторые нет. И, конечно, это делает некоторые новые.

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

  2. Утечки памяти - это не так важно для современного C ++, но для начинающих и в течение примерно половины своего срока службы C ++ значительно упростил утечку памяти. Эффективный C ++ полностью соответствовал эволюции практик для устранения этой проблемы. Тем не менее, C # все еще может утечка памяти. Наиболее распространенный случай, с которым сталкиваются люди, - это захват событий. Если у вас есть объект, и вы поместили один из его методов в качестве обработчика события, владелец этого события должен быть GC'd, чтобы объект умер. Большинство новичков не понимают, что обработчик событий считается ссылкой. Существуют также проблемы с неиспользованием одноразовых ресурсов, которые могут привести к утечке памяти, но они встречаются не так часто, как указатели в до-эффективном C ++.

  3. Компиляция - C ++ имеет модель компиляции с задержкой. Это приводит к целому ряду хитростей, позволяющих с ним хорошо играть, и сокращает время компиляции.

  4. Строки - Современный C ++ делает это немного лучше, но char*он ответственен за ~ 95% всех нарушений безопасности до 2000 года. Для опытных программистов, они сосредоточатся на этом std::string, но это все же что-то, чтобы избежать и проблема в старых / худших библиотеках , И это молитва, что вам не нужна поддержка Unicode.

И действительно, это верхушка айсберга. Основная проблема в том, что C ++ - очень плохой язык для начинающих. Это довольно противоречиво, и многие старые, действительно, очень плохие ловушки были исправлены путем изменения идиом. Проблема в том, что начинающим нужно учить идиомы чему-то вроде Effective C ++. C # устраняет многие из этих проблем в целом, а остальное меньше заботит, пока вы не продвинетесь дальше по пути обучения.

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

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

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

Еще одна подводная лодка, с которой сталкиваются программисты, это семантика захвата анонимных функций. Когда вы захватываете переменную, вы захватываете переменную . Пример:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

Не делает то, что наивно думают. Это печатает 1010 раз.

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


4
Утечки памяти остались в прошлом, и это так char*. Не говоря уже о том, что вы все еще можете утечь память в C # просто отлично
DeadMG

2
Называть шаблоны "прославленной вставкой строк" немного. Шаблоны действительно одна из лучших функций C ++.
Чарльз Сальвия

2
@CharlesSalvia Конечно, они действительно отличительная особенность C ++. И да, это, возможно, упрощение для воздействия компиляции. Но они непропорционально влияют на время компиляции и размер вывода, особенно если вы не будете осторожны.
Теластин

2
@deadMG, конечно, хотя я бы сказал, что многие приемы метапрограммирования шаблонов, используемые / необходимые в C ++, ... лучше реализуются с помощью другого механизма.
Теластин

2
@Telastyn весь смысл type_traits состоит в том, чтобы получать информацию о типе во время компиляции, чтобы вы могли использовать эту информацию для определенных вещей, таких как специализированные шаблоны или функции перегрузки, используяenable_if
Charles Salvia

10

На мой взгляд, опасность C ++ несколько преувеличена.

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

Эта, так сказать, «лишняя веревка», о которой говорил Джоэл Спольски , сводится к одному: написание классов, которые внутренне управляют своей собственной памятью, также известной как « правило 3 » (которое теперь можно назвать правилом 4 или Правило 5 в C ++ 11). Это означает, что если вы когда-нибудь захотите написать класс, который управляет своими собственными распределениями памяти внутри, вы должны знать, что вы делаете, иначе ваша программа, скорее всего, потерпит крах. Вы должны тщательно создать конструктор, конструктор копирования, деструктор и оператор присваивания, что на удивление легко ошибиться, что часто приводит к причудливым сбоям во время выполнения.

ОДНАКО в реальном повседневном программировании на C ++ действительно очень редко пишут класс, который управляет собственной памятью, поэтому вводить в заблуждение то, что программисты на C ++ всегда должны быть «осторожными», чтобы избежать этих ловушек. Обычно вы будете делать что-то более похожее на:

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Этот класс выглядит очень похоже на то, что вы делаете в Java или C # - он не требует явного управления памятью (потому что библиотечный класс std::stringпозаботится обо всем этом автоматически), и никаких «Правил 3» вообще не требуется, так как по умолчанию Конструктор копирования и оператор присваивания в порядке.

Это только когда вы пытаетесь сделать что-то вроде:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

В этом случае новичкам может быть сложно получить правильное назначение, деструктор и конструктор копирования. Но в большинстве случаев нет причин когда-либо делать это. C ++ позволяет очень легко избежать ручного управления памятью в 99% случаев с помощью библиотечных классов, таких как std::stringи std::vector.

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

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Если на some_function_which_may_throw()самом деле это сгенерирует исключение, вы остаетесь с утечкой памяти , потому что память , выделенная для sникогда не будет утилизирована. Но опять же, на практике это вряд ли проблема больше по той же причине, по которой «правило 3» больше не является проблемой. Очень редко (и обычно не нужно) фактически управлять своей собственной памятью с помощью сырых указателей. Чтобы избежать вышеуказанной проблемы, все, что вам нужно сделать, это использовать std::stringили std::vector, и деструктор будет автоматически вызываться при разматывании стека после возникновения исключения.

Итак, общая тема здесь заключается в том, что многие функции C ++, которые не были унаследованы от C, такие как автоматическая инициализация / уничтожение, конструкторы копирования и исключения, вынуждают программиста быть особенно осторожным при ручном управлении памятью в C ++. Но опять же, это проблема только в том случае, если вы собираетесь в первую очередь выполнять ручное управление памятью, что вряд ли когда-либо понадобится, когда у вас есть стандартные контейнеры и умные указатели.

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


Это было правило трех в C ++ 03 и теперь правило четырех в C ++ 11.
DeadMG

1
Вы можете назвать это «Правило 5» для конструктора копирования, конструктора перемещения, назначения копирования, назначения перемещения и деструктора. Но семантика перемещения не всегда необходима только для правильного управления ресурсами.
Чарльз Сальвия

Вам не нужно отдельно перемещать и копировать назначение. Идиома копирования и замены может выполнять оба оператора в одном.
DeadMG

2
Это отвечает на вопрос Does C# avoid pitfalls that are avoided in C++ only by careful programming?. Ответ «не совсем, потому что банально легко избежать ловушек, о которых говорил Джоэл в современном C ++»
Чарльз Сальвия

1
IMO, в то время как языки высокого уровня, такие как C # или Java, предоставляют вам управление памятью и другие вещи, которые должны вам помочь , это не всегда делает так, как предполагалось. Вы по-прежнему заботитесь о дизайне своего кода, поэтому не оставляете утечки памяти (что не совсем то, что вы бы назвали в C ++). Исходя из моего опыта, мне ДАЖЕ легче управлять памятью в C ++, потому что вы знаете, что будут вызываться деструкторы, и в большинстве случаев они выполняют очистку. В конце концов, C ++ имеет умные указатели для случаев, когда дизайн не позволяет эффективно управлять памятью. C ++ отлично подходит, но не для чайников.
Pijusn

3

Я бы не очень согласился. Возможно, меньше подводных камней, чем в C ++, как это было в 1985 году.

C # избегает ловушек, которых избегают в C ++ только при тщательном программировании? Если да, то в какой степени и как их избежать?

На самом деле, нет. Правила , как правило три потеряли массивное значение в C ++ 11 , благодаря unique_ptrи shared_ptrбыть стандартизированы. Использование стандартных классов в некоторой степени разумным способом - это не «тщательное кодирование», а «базовое кодирование». Кроме того, доля населения C ++, который все еще достаточно глуп, не информирован или выполняет обе функции, такие как ручное управление памятью, намного ниже, чем раньше. Реальность такова, что лекторам, которые хотят продемонстрировать подобные правила, приходится тратить недели, пытаясь найти примеры, где они все еще применяются, потому что стандартные классы охватывают практически все мыслимые варианты использования. Многие эффективные техники С ++ пошли тем же путем - путем додо. Многие другие не настолько специфичны для C ++. Дайте-ка подумать. Пропустив первый пункт, следующие десять:

  1. Не кодируйте C ++, как это C. Это на самом деле просто здравый смысл.
  2. Ограничьте свои интерфейсы и используйте инкапсуляцию. ООП.
  3. Авторы двухфазного кода инициализации должны быть сожжены на костре. ООП.
  4. Знать, что такое семантика значения. Это действительно C ++?
  5. Снова ограничьте свои интерфейсы, на этот раз немного по-другому. ООП.
  6. Виртуальные деструкторы. Да. Этот, вероятно, все еще действует. finalи overrideпомогли изменить эту игру к лучшему. Сделайте свой деструктор, overrideи вы гарантированно получите хорошую ошибку компилятора, если унаследуете от того, кто не сделал его деструктор virtual. Создайте свой класс, finalи ни один плохой скраб не сможет прийти и наследовать его случайно без виртуального деструктора.
  7. Плохие вещи случаются, если не удается очистить функции. Это не совсем специфично для C ++ - вы можете увидеть один и тот же совет как для Java, так и для C # - и, в общем, почти для каждого языка. Имея функции очистки, которые могут потерпеть неудачу, просто плохо, и в этом нет ничего C ++ или даже ООП.
  8. Знайте, как порядок конструктора влияет на виртуальные функции. Весело, в Java (текущей или прошлой) он просто некорректно вызвал бы функцию производного класса, что даже хуже, чем поведение C ++. Несмотря на это, эта проблема не относится к C ++.
  9. Перегрузки оператора должны вести себя так, как ожидают люди. Не совсем конкретный. Черт, это даже не специфическая перегрузка операторов, то же самое можно применить к любой функции - не называйте ее одним именем, а затем заставляйте делать что-то совершенно неинтуитивное.
  10. На самом деле это сейчас считается плохой практикой. Все операторы присваивания, исключающие строгие исключения, прекрасно справляются с самопредставлением, и самопредставление фактически является логической программной ошибкой, и проверка самоназначения просто не стоит затрат производительности.

Очевидно, я не собираюсь проходить каждый элемент Effective C ++, но большинство из них просто применяют базовые концепции к C ++. Тот же совет вы найдете в любом объектно-ориентированном языке перегрузочных операторов с типизированным значением. Виртуальные деструкторы - это единственная ловушка в C ++, и она по-прежнему действительна, хотя, возможно, с finalклассом C ++ 11 она не так верна, как была. Помните, что Effective C ++ был написан, когда идея применения ООП и специфических особенностей C ++ была еще очень новой. Эти пункты вряд ли о подводных камнях C ++ и больше о том, как справиться с изменением от C и как правильно использовать ООП.

Изменить: Подводные камни C ++ не включают в себя такие вещи, как подводные камни malloc. Я имею в виду, что, во-первых, каждую ловушку, которую вы можете найти в C-коде, вы также можете найти в небезопасном C # -коде, так что это не особенно актуально, и, во-вторых, только потому, что Стандарт определяет его для взаимодействия, не означает, что его использование считается C ++. код. Стандарт также определяет goto, но если бы вы написали огромную кучу беспорядка спагетти, используя его, я бы подумал, что ваша проблема, а не язык. Существует большая разница между «тщательным кодированием» и «следованием основным идиомам языка».

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

usingотстой. Это действительно так. И я понятия не имею, почему что-то лучшее не было сделано. Кроме того, Base[] = Derived[]и почти каждое использование Object, которое существует потому, что первоначальные дизайнеры не заметили огромного успеха шаблонов в C ++, и решили, что «Давайте просто все унаследуем от всего и потеря всей нашей безопасности типов» было более разумным выбором. , Я также считаю, что вы можете найти некоторые неприятные сюрпризы в таких вещах, как условия гонки с делегатами и другие подобные забавы. Есть и другие общие вещи, например, как дженерики ужасно сосут по сравнению с шаблонами, действительно действительно ненужное принудительное размещение всего в a classи тому подобное.


5
Образованная база пользователей или новые конструкции на самом деле не уменьшают веревку. Они просто обходные пути, поэтому меньше людей в конечном итоге зависают. Хотя это хороший комментарий к Effective C ++ и его контексту в эволюции языка.
Теластин

2
Нет. Речь идет о том, как множество элементов в Effective C ++ являются концепциями, которые могут в равной степени применяться к любому объектно-ориентированному языку с типизированным значением. А обучение пользовательской базы кодированию реального C ++ вместо C, безусловно, уменьшает веревку, которую дает вам C ++. Кроме того , я хотел бы ожидать , что новые языковые конструкции являются убывающой веревкой. Речь идет о том, что то, что определяет стандарт C ++, mallocвовсе не означает, что вы должны это делать, и даже не потому, что вы можете быть шлюхой, gotoкак сукой, это означает, что веревкой вы можете повеситься.
DeadMG

2
Использование C-частей C ++ ничем не отличается от написания всего вашего кода unsafeна C #, что так же плохо. Я мог бы перечислить каждую ловушку кодирования C # как C, если хотите.
DeadMG

@DeadMG: на самом деле вопрос должен звучать так: «У программиста на С ++ достаточно веревки, чтобы повеситься, пока он программист на С»
gbjbaanb

«Кроме того, доля населения C ++, которые все еще достаточно глупы, неосведомлены или и то и другое для выполнения таких задач, как ручное управление памятью, намного ниже, чем раньше». Нужна цитата.
Ден04

3

C # избегает ловушек, которых избегают в C ++ только при тщательном программировании? Если да, то в какой степени и как их избежать?

C # имеет следующие преимущества:

  • Не имеет обратной совместимости с C, что позволяет избежать длинного списка «злых» языковых возможностей (например, необработанных указателей), которые синтаксически удобны, но теперь считаются плохим стилем.
  • Наличие ссылочной семантики вместо семантики значений, что делает спор как минимум 10 из элементов Effective C ++ (но вводит новые ловушки).
  • Имеет меньшее поведение, определяемое реализацией, чем C ++.
    • В частности, в C ++ кодировка символов char, stringи т.д. зависит от реализации. Раскол между подходом Windows к Unicode ( wchar_tдля UTF-16, charдля устаревших «кодовых страниц») и подходом * nix (UTF-8) вызывает большие трудности в кроссплатформенном коде. C #, OTOH, гарантирует, что это stringUTF-16.

Есть ли в C # новые подводные камни, о которых должен знать новый программист C #?

Да: IDisposable

Есть ли книга, эквивалентная "Эффективному C ++" для C #?

Есть книга под названием Effective C #, которая похожа по структуре на Effective C ++ .


0

Нет, C # (и Java) менее безопасны, чем C ++

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

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C #:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C #:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.

3
... в современных IDE довольно просто определить, что-то наследует от IDisposable. Основная проблема заключается в том, что вам нужно знать, чтобы использовать auto_ptr(или несколько его родственников). Это общеизвестная веревка.
Теластин

2
@Telastyn нет, дело в том, что вы всегда используете умный указатель, если только вы действительно не знаете, что он вам не нужен. В C # оператор using похож на веревку, на которую вы ссылаетесь. (т.е. в C ++ вы должны помнить об использовании умного указателя, почему C # не так уж плох, хотя вы должны всегда помнить использование оператора using)
gbjbaanb

1
@gbjbaanb Потому что что? 5% в большинстве классов C # являются одноразовыми? И вы знаете, что вам нужно избавиться от них, если они одноразовые. В C ++ каждый отдельный объект является одноразовым. И вы не знаете, нужно ли разбираться с вашим конкретным случаем. Что происходит с возвращаемыми указателями, которые не с фабрики? Вы несете ответственность за их очистку? Так не должно быть, но иногда это так. И опять же, то, что вы всегда должны использовать умный указатель, не означает, что опция не перестает существовать. Особенно для начинающих, это серьезная ошибка.
Теластин

2
@Telastyn: знать, как использовать, auto_ptrпросто знать IEnumerableили использовать интерфейсы, или не использовать плавающую точку для валюты или чего-то подобного. Это базовое приложение DRY. Никто, кто знает основы программирования, не допустит этой ошибки. В отличие от using. Проблема в usingтом, что вам нужно знать для каждого класса, является ли он Одноразовым (и я надеюсь, что он никогда не изменится), и если он не Одноразовый, вы автоматически запрещаете все производные классы, которые могут быть Одноразовыми.
DeadMG

2
Кевин: Твой ответ не имеет смысла. Это не вина C #, что вы делаете это неправильно. Вы же НЕ зависите от финализаторов в правильно написанных C # код . Если у вас есть поле с Disposeметодом, вы должны реализовать его IDisposable(«правильный» способ). Если ваш класс делает это (что эквивалентно реализации RAII для вашего класса в C ++), и вы используете using(что похоже на интеллектуальные указатели в C ++), все это прекрасно работает. Финализатор в основном предназначен для предотвращения несчастных случаев - Disposeотвечает за правильность, и если вы его не используете, то это ваша вина, а не C #.
user541686

0

Да, 100%, да, так как я думаю, что невозможно освободить память и использовать ее в C # (при условии, что она управляемая, и вы не переходите в небезопасный режим).

Но если вы знаете, как программировать на C ++, чего не знает невероятное количество людей. Вы в значительной степени в порядке. Как Чарльз Сальвия, классы на самом деле не управляют своей памятью, поскольку все это обрабатывается в уже существующих классах STL. Я редко использую указатели. На самом деле я работал над проектами без единого указателя. (C ++ 11 делает это проще).

Что касается опечаток, глупых ошибок и т. Д. (Например: if (i=0)ключ застрял, когда вы нажали == очень быстро), компилятор жалуется, что приятно, так как улучшает качество кода. Другой пример - забывать breakв операторах switch и не разрешать вам объявлять статические переменные в функции (что мне иногда не нравится, но это хорошая идея imo).


4
Java и C # усугубили проблему =/ ==, использовав ==для сравнения ссылок и введя .equalsдля равенства значений. Плохой программист теперь должен отслеживать, является ли переменная «double» или «Double», и обязательно вызывать правильный вариант.
Кевин Клайн

@kevincline +1, но в C # structвы можете сделать это, ==что работает невероятно хорошо, так как в большинстве случаев будут только строки, целые и плавающие числа (то есть только члены структуры). В моем собственном коде я никогда не получаю эту проблему, за исключением случаев, когда я хочу сравнить массивы. Я не думаю, что когда-либо сравнивал список или неструктурные типы (string, int, float, DateTime, KeyValuePair и многие другие)

2
Python понял это правильно, используя ==для равенства значений и isдля ссылочного равенства.
Ден04

@ dan04 - Как вы думаете, сколько типов равенства есть в C #? Посмотрите отличную молниеносную беседу ACCU: некоторые объекты более равны, чем другие
Марк Бут
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.