| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Общие вопросы по .NET и C# > .NET глазами дельфийца. C# |
| Автор: Medved 21.12.2004, 09:13 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .NET глазами дельфийца. C# опубликовано: 27.03.2002 14:46 Учитывая то, что C#, как и Delphi, выступает одновременно в двух качествах, т.е. с одной стороны, является семантически строго определенным языком программирования и, с другой стороны, использует поставляемые в составе .Net библиотеки классов и компонентов, на первом этапе имеет смысл сконцентрироваться на самом языке программирования, т.к. изучение и сравнительный анализ библиотек классов - гораздо более объемная работа. .Net глазами дельфийца - первые впечатления. Введение При знакомстве с новым языком программирования любого программиста в первую очередь интересует семантическая основа языка, т.е. насколько его выразительные возможности позволяют реализовать привычные логические конструкции. Учитывая то, что C#, как и Delphi, выступает одновременно в двух качествах, т.е. с одной стороны, является семантически строго определенным языком программирования и, с другой стороны, использует поставляемые в составе .Net библиотеки классов и компонентов, на первом этапе имеет смысл сконцентрироваться на самом языке программирования, т.к. изучение и сравнительный анализ библиотек классов - гораздо более объемная работа. Чего нет в C# Отсутствие в C# некоторых вещей обусловлено тем, что C# является <чисто> объектным языком программирования, а Delphi - гибридным. Тем не менее, в C# или имеются, или могут быть легко реализованы самостоятельно практически все семантически эквивалентные конструкции. Итак, C# не предоставляет следующие возможности (их рассмотрение не вошло в настоящий документ в силу или второстепенного значения, или наличия семантически эквивалентных реализаций в библиотеке CLR):
Если считать, что процедуры - это просто функции, которые не возвращают никакого значения, то семантическая нагрузка процедур и функций в Delphi одинакова. Это - выполнение некоторого фрагмента кода, который, возможно, зависит от входных параметров:
В C# семантическим эквивалентом процедур и функций выступают статические методы классов.
Глобальные константы Семантическая нагрузка в Delphi - определение значений примитивных типов данных, доступных из любого места кода и неизменяемых в процессе выполнения программы.
Семантический эквивалент в C# - статические константы.
Кроме статических констант C# предоставляет механизм статических полей <только для чтения>, который позволяет программисту использовать в качестве констант не только примитивные значения, но и объекты. Пример кода:
Глобальные переменные Семантическая нагрузка в Delphi - формирование объектов программы (как примитивных типов, так и сложных), доступных из любого места кода и, возможно, изменяемых в процессе выполнения программы.
Семантический эквивалент в C# - статические поля классов.
Предварительное объявление типов Предварительное объявление типов на самом деле не предусмотрено общей теорией объектно-ориентированного программирования и является частным решением Delphi, направленным на ослабление правила, которое было введено еще в классическом Pascal, - <все типы данных, используемые для построения сложных типов, должны быть или примитивного типа, или описаны до их использования>. Пример кода на Delphi:
В C# предописание типов не требуется, т.к. в пределах области видимости классов (обрамляющий класс, пространство имен) порядок объявления несущественен. Такое решение упрощает написание кода:
Типизированные константы Типизированные константы в Delphi позволяют хранить не только значения примитивных типов, но и массивы, записи, а также указатели, включая указатели на процедуры и функции:
В C# константы всегда типизированы - как при использовании модификатора const, так и readonly. В Delphi при использовании директивы компилятора {$J+} (установлено по умолчанию) типизованные константы ведут себя как обычные переменные, которые инициализируются одновременно с описанием, т.е. их значение может быть изменено в ходе выполнения программы:
В C# действуют более строгие правила - если константа, то поменять ее значение невозможно. Если же используется поле <только для чтения>, то его содержимое может быть изменено в контексте объекта:
При использовании типизованных констант в качестве инициализируемых переменных в области видимости подпрограммы (метода, процедуры, функции) в Delphi наблюдается недокументированный побочный эффект - данные, записанные в локальные типизованные константы, сохраняются между вызовами подпрограмм:
Результаты вывода: 1, 2, 3 В C# подобный побочный эффект отсутствует. Вообще, стандартизация C# в качестве международного стандарта (ECMA, 2001 год) гарантирует отсутствие побочных эффектов. Поэтому если в программе C# необходимо сохранять некоторое состояние между вызовами подпрограмм (методов), используются стандартные средства, в частности, статические или обыкновенные поля классов. Const-параметры В Delphi семантический смысл const-параметров заключается в указании компилятору на возможность оптимизации передачи в функцию (процедуру, метод) неизменяемой ссылки на некоторый объект программы. Так, например, конструкция типа:
означает, что в качестве параметра процедуры будет передаваться ссылка на строку (при этом копирования содержимого строки в стек вызова процедуры не происходит). Кроме того, содержимое строки S внутри процедуры изменить нельзя. В C# не предусмотрено прямого эквивалента const-параметров. Тем не менее, в случае необходимости может быть построена семантически эквивалентная конструкция (аналогия вышеприведенному примеру):
Приведенный код иллюстрирует использование классов-<оберток> (т.н. wrappers) и полей <только для чтения>. Указатели В Delphi указатели чаще всего используются для <тонкого> управления такими конструкциями, как записи. В частности, при передаче записи в качестве параметра в подпрограмму (процедуру, функцию или метод) происходит побайтное копирование в стек вызова, что может приводить к серьезным накладным расходам. Альтернативное решение - передать в качестве параметра указатель на запись и уже через него внутри подпрограммы получить доступ к элементам записи. В C# приведенный пример использования указателей на записи более изящно и безопасно реализуется с использованием такой конструкции, как struct. Структуры, как и записи Delphi, могут использоваться для хранения данных и, что более важно с точки зрения семантики, являются объектами, передаваемыми не по ссылке, а по значению. На самом деле, в C# имеется всего лишь одна возможность использовать указатели - т.н. <небезопасный> код (unsafe code). Особенности его использования определены в стандарте C# достаточно подробно (спецификация C#, приложение A). Однако, необходимо отметить, что в практическом программировании редко приходится использовать указатели для иных целей, кроме оптимизации производительности. Поэтому, учитывая классическое правило <80/20> (<80% пива выпиваются двадцатью процентами населения>, или <80% используемых ресурсов программы приходится на 20% кода>, или <80% объема работы позволяет улучшить производительность только на 20%>), можно акцентироваться на оптимизации кода (в терминах C# - использование небезопасного кода) только при необходимости и только тогда, когда выявлены те самые 20% кода, которые используют 80% ресурсов. Чего нет в Delphi Теперь можно рассмотреть те преимущества, которые имеет C# по сравнению с Delphi (порядок перечисления произволен и ни в коей мере не отражает объективные приоритеты или субъективные предпочтения):
Возможность описания переменных в коде программы пришла в C# из C++. Пример кода:
В этом примере продемонстрированы сразу несколько возможностей, отсутствующих в Delphi:
Пример кода, иллюстрирующий возможность передачи в метод переменного количества параметров:
В Delphi отсутствие переменного количества параметров можно частично скомпенсировать либо использованием значений параметров по умолчанию, либо передачей в качестве параметра открытого массива. Однако в первом случае все равно количество параметров ограничено и должно быть определено при написании соответствующей подпрограммы. Во втором же случае, хотя и наблюдается сходство с C#, есть существенные ограничения:
При программировании в терминах объектов Delphi приходится постоянно учитывать, когда создаются объекты определенного класса и, что еще важнее, кто и когда их удаляет. Ситуация несколько упрощается при построении визуальных приложений и использовании компонентов - установив компонент на форму (или модуль данных), тем самым мы определяем механизм создания и удаления объектов соответствующего класса, когда форма-владелец создает объект в процессе своей инициализации и удаляет его в момент своего удаления. При построении невизуальных приложений (например, реализация бизнес-правил на промежуточном слое многоуровневого приложения) можно использовать механизм автоматического удаления COM-объектов или <интерфейсных> объектов (объектов, являющихся наследниками от TInterfacedObject). Впрочем, необходимо признать, что автоматическое удаление объектов в Delphi реализовано не лучшим образом, т.к.:
Рассмотрим простую семантическую конструкцию - загрузку списка объектов. В Delphi типичная реализация выглядят примерно так:
На самом деле из-за ограничений Delphi (TObjectList не может удаляться автоматически) семантика приведенного кода разбивается на две отдельные фазы:
Строго говоря, в приведенном коде C# даже два преимущества:
Поля классов <только для чтения> В Delphi для того, чтобы реализовать концепцию <поле только для чтения>, можно использовать свойства (properties), при этом приходится писать нечто подобное:
Поля <только для чтения> в C# введены на уровне языка:
Разница между (статическими) константами и полями <только для чтения> заключается в том, что если константы могут быть вычислены на стадии компиляции, что справедливо, например, для простых типов, то значения полей <только для чтения> определяются только на стадии выполнения программы. Это приводит к интересным последствиям. В стандарте C# рассматривается ситуация, когда имеется библиотека и использующая ее программа, компилируемые раздельно. Если в библиотеке использовать константу, то при изменении ее значения (и перекомпиляции библиотеки) нужно перекомпилировать и программу. Если же использовать поле <только для чтения>, то программу перекомпилировать не обязательно, т.к. значение поля определяется на стадии исполнения. Индексаторы В Delphi можно реализовать свойство класса типа массив и, установив для него атрибут default, получить некоторое подобие индексатора:
Тогда в коде можно использовать две эквивалентные конструкции:
Вторая строка как раз и демонстрирует основную идею индексаторов C# - возможность обращаться к объекту как к массиву. Однако в Delphi есть существенное ограничение - можно использовать только одно свойство (типа массива) по умолчанию. В C# можно реализовать произвольное количество индексаторов для класса:
Делегаты В Delphi обработчики событий играют роль делегатов - <делегируют> реальную работу внешнему объекту. Однако из-за того, что Delphi является гибридным языком программирования, встречаются ситуации, когда семантически эквивалентные задачи реализуются разными способами. Так, например, в класса TList при сортировке используется указатель на функцию сравнения элементов:
Т.е. на самом деле задача сравнения элементов списка также <делегируется> функции, внешней по отношению к объекту TList. Такая семантическая неоднозначность отнюдь не упрощает программирование. В C# реализован более общий и однозначный подход - делегаты:
Причем в качестве реализации делегата может выступать как статический, так и обычный метод. Возможность реализации модели обработки событий <один-много> Хотя по умолчанию в C# компоненты реализуют схему подключения обработчиков событий <один-к-одному>, при необходимости может быть реализована модель <один-много>. Основа такой возможности заключается в том, что в качестве обработчиков событий используются делегаты, а их подключение к компоненту реализуется с помощью перегружаемого метода:
Приведенный код взят из спецификации C# и, хотя выглядит относительно объемным, прозрачно иллюстрирует возможность использования произвольного внутреннего механизма для хранения ссылок на обработчики и подключения произвольного количества внешних обработчиков. Цикл foreach Цикл foreach перенят в C# из Visual Basic. Получилась довольно удобная вещь:
Семантически аналогичный код в Delphi выглядит более громоздким из-за необходимости использовать итератор (переменная I), а также (в общем случае) вычислять границы массива:
Статические конструкторы Некоторый семантический аналог статическим конструкторам в Delphi - секция initialize. К сожалению, в Delphi порядок вызова секций initialize соответствует порядку подключения модулей. Такая практика может приводить к неожиданным ошибкам - первоначально рассчитывая на конкретный порядок подключения модулей, можно случайно в процессе разработки изменить этот порядок и в результате, например, получить обращение к несуществующему или некорректно инициализированному глобальному объекту программы. C# предоставляет более строгое объектное решение, которое, в частности, позволяет управлять правами доступа:
В C# порядок работы статического конструктора определен только на уровне класса, при наличии же нескольких классов со статическими конструкторами порядок их активизации не фиксирован. Такой подход заставляет более тщательно проектировать программу с самого начала и исключает появление в последующем ошибок, аналогичных описанной выше. Операторы классов Операторы классов в C# почти эквивалентны операторам классов в C++:
По сравнению с C++ в C# строго и однозначно определен порядок реализации пользовательских правил преобразования объектов (преобразования рассматриваются как частный случай операторов). Примечание: Delphi не имеет механизмов, эквивалентных операторам классов. Структуры Структуры в C# аналогичны записям в Delphi в том смысле, что являются данными, передаваемыми по значению, а не по ссылке. На самом деле семантика структур в C# ближе к классам, за исключением двух основных ограничений:
Использование структур может как повысить производительность программы (например, при размещении большого количества мелких объектов лучше использовать структуры), так и ухудшить ее (если используется структура, содержащая большие объемы данных, то при передаче ее в качестве параметра будет выполняться лишнее копирование). Существует эмпирическое правило: если объем данных меньше 16 байт, то для их хранения лучше использовать структуру, если больше - класс. Атрибуты Интереснейшая возможность C#, отсутствующая как в Delphi, так и в других наиболее популярных языках программирования (VB, C++, Java), - атрибуты:
Атрибуты похожи на свойства классов Delphi, за исключением того, что их значения устанавливаются на стадии компиляции и в процессе выполнения программы могут быть только считаны. Однако сфера применения атрибутов в поставляемой библиотеке классов CLR весьма широка - от хранения вспомогательной информации декларативного характера до обеспечения совместимости объектов .Net с COM (атрибуты совместимости с COM описаны в приложении B спецификации C#). Привычки, сформированные под влиянием Delphi, не позволяют даже сразу придумать, для чего можно использовать атрибуты, однако поле деятельности здесь просматривается широкое - от отслеживания версий алгоритмов до контроля за совместимостью программ. Возможность использовать русский язык для имен объектов программы В соответствии со спецификацией C# для именования объектов программы (классы, методы, переменные и пр.) используются символы Unicode. Отсюда следует (и реально проверено), что в качестве имен можно использовать русские названия, например:
Конечно, использование русского языка в коде программы - вопрос спорный. Тем не менее, такая возможность есть. Использовать же ее или нет - вопрос стандартов написания кода в пределах рабочей группы (отдела, предприятия, корпорации). Заключение Безусловно, изложенные выше моменты не могут претендовать на абсолютную полноту и глубокую детализацию. Тем не менее, даже на их основе напрашивается естественный вывод - C# является весьма интересным языком программирования, по сравнению с Delphi во многих аспектах даже более мощным и в то же время строгим. Можно ли на нем писать реальные промышленные задачи? Если ориентироваться на монолитные приложения в стиле АРМ, то скорее всего нужно более детально проанализировать возможности исполнения программ .Net на конкретной аппаратной платформе (навряд ли Framework.Net сможет работать на 486 с 8М ОЗУ). Если же сделать акцент на многоуровневых приложениях, то C# для реализации промежуточного слоя по сравнению с Delphi является более перспективным кандидатом. Общий ответ на поставленный вопрос - писать реальные задачи на C# вполне можно, и, скорее всего, даже с меньшими затратами, чем на Delphi. Трудно ли дельфийцу освоить C#? Можно утверждать, что нет, т.к. в основе и Delphi, и C# лежит парадигма объектно-ориентированного программирования. Программист, который может в Delphi писать код в терминах объектов, а не только на уровне обработчиков стандартных компонентов, сможет писать и на C#. Впрочем, C# - не единственный язык, который можно использовать в .Net. Даже в штатной поставке Visual Studio .Net вместе с C# идет C++ и VB.Net, а вообще список языков программирования, реализованных для платформы .Net, уже на сегодня перевалил за десяток. Может быть, и Borland когда-нибудь выпустит Delphi.Net - не зря ведь <отцом> C# является как раз автор Delphi. Ждать ли дельфийцам выхода Delphi.Net? На этот вопрос может ответить только фирма Borland. На самом деле, если глубже поразбираться в вопросе декларируемой совместимости программ, написанных на Delphi 6 и Kylix, то оказывается, что о полной совместимости речь не идет - даже у одноименных классов VCL и CLX встречаются разные интерфейсы. Поэтому можно предположить, что Delphi.Net будет совместима с <обычной> Delphi тоже не на все 100%. В чем же тогда будет преимущество Delphi.Net перед тем же C#? Для <закостенелого> дельфийца - только в знакомом синтаксисе. Ну а пока Delphi.Net выйдет в свет, можно и C# освоить (тем более, что его корни лежат в C++, а C++ всегда считался более мощным языком, чем Pascal), или можно на VB.Net писать. Но делать это нужно уже СЕГОДНЯ. Источник http://www.gotdotnet.ru/LearnDotNet/CSharp/732.aspx |
| Автор: Medved 21.12.2004, 11:48 | ||
To же самое, но в другом стиле:
|
| Автор: Dayana 22.12.2004, 23:16 |
| Интересные статьи! Я на них не натыкалась в инете. Как раз сейчас изучаю C#. Полезно было почитать. А можно перенести эту тему в раздел .Net и закрепить ее наверху? |
| Автор: Medved 23.12.2004, 14:10 |
| ОК. |
| Автор: Cheba 24.12.2004, 00:59 | ||
| Pegas, а ведь Delphi на месте тоже не стоит. Я только начал ковырять Borland Developer Studio 2005 (v3 который). Там тоже есть интересные фишки. Вот, буквально за три минуты нашел. Нужна форма с двумя кнопками. и Копи-пейст. Ну, сами, думаю, разберетесь...
И это все прекрасно компилится. И Даже работает. |
| Автор: Medved 28.12.2004, 00:15 |
| Это хорошо. Но программировать на C# гораздо эффективней и бысрее чем в Delphi. |
| Автор: Quadri 11.1.2005, 15:26 | ||
Гораздо эффективней - что это значит - программа будет работать быстрее? быстрее чем в Delphi - имеется ввиду скорость разработки? Неужели? |
| Автор: AntonSaburov 11.1.2005, 15:37 | ||
Ну то, что быстрее - это надо смотреть код на MSIL, который будет скомпилирован из Pascal. То, что разработка будет быстрее - тоже не факт, кому чем удобнее. Но как язык C# имеет явные удобства, которых нет в ObjectPascal. Переменные объявлять, цикл по коллекциям и спискам foreach. Хотя опять же это достаточно субъективное мнение. Кому-то нравиться чистый Си и всякие ОО ему до лампочки. |
| Автор: Cheba 11.1.2005, 17:46 | ||
Мда... А тремя постами выше о чем речь идет? |
| Автор: AntonSaburov 11.1.2005, 18:01 | ||
Не понял. Если я хочу ввести переменную для использования в цикле, то в Pascal мне надо бежать в начало функции и описывать прямо там. Это уже давняя болезнь. И она меня уже лично достает не первый год. foreach - тут лопухнулся, не смотрел. Согласен |
| Автор: Cheba 11.1.2005, 23:18 |
| AntonSaburov Ох не знаю, не знаю... Как говорится, каждому свое... Я вот тут лабораторки сдавал по С++... СОбственно я его семестр как изучаю, а Delphi (pascal) с восьмого класа учу. Так это для меня такая проблема была - переменные... Сначала писал, как все сишники и как препод учил. Но потом забил на все и начал выписывать все перемменные в начале программы/функции. От хочшь верь, хочешь не верь, но сразу кучу ошибок нашел. Ну, не могу я так. Теряю я эти переменные. Хотя это скорее всего привычка... |
| Автор: dm9 12.1.2005, 05:58 |
| Я тоже был фанатом Паскаля. Си я, честно говоря, толком не знаю, хотя с базовым синтаксисом, конечно, знаком. А вот на PHP кое-что пришлось пописать. Javascript вот тоже пробую. Честно скажу - после этих языков Delphi кажется жутко громоздким. Могу понять Пегаса про то, что на Delphi разработка идёт медленнее. |
| Автор: Ser9a 7.3.2005, 11:03 |
| Все зависит не только от предподчтения разработчиков, но и от веяний рынков. Моя б воля вжизни с Дельфей не слез бы. Но надо значит надо. Delphi конечно на месте не стоит, но работодатели то смотрят в сторону Майкрософта. |
| Автор: Cr@$h 10.4.2005, 18:40 | ||
Ага, щас. Все смотрят в сторону билдеров, как раз... но это уже |
| Автор: Balu 25.7.2005, 18:19 | ||||
Это не совсем верно, константа определяется как: <область видимости> const <тип> название |
| Автор: Void 25.7.2005, 20:46 |
| Balu Речь идет о том, что в C# нет понятия deep immutability. Т.е. если объект reference-типа содержит методы, изменяющие его состояние, то нет никаких способов, передав его в качестве параметра метода, защитить его от изменения. Приходиться писать wrapper. |
| Автор: sgi1981 15.7.2006, 00:00 |
| А вот мне лично нравится ассемблер. Я много раз занимался тем, что переводил разные фрагменты программы в ассемблерные вставки, а переведенные фрагменты закомментировал. И программа выполнялась в 1.5 раза быстрее ! Потому что процессор выполнял мой код а не компилеровский. Пускай там что хотят пишут про разные языки, про удобства или неудобства их использования, а мне как то по барабану какой там синтаксис. Железо не знает синтаксиса. Хех. Говорят. "А вот давайте посмотрим, как это сделано в C++, а как в C#". Ну давайте посмотрим. Смешно, блин, смотреть от мысли что все равно кристалл процессора выполняет микрооперации. Для тех кто не знает что такое микрооперация могу сообщить новость. Процессор декодирует машинные инструкции в микрооперации, то есть ещё больше разбивает код. Вот. так вот я как посмотрел в окно CPU и стало не в кайф - какой же там несовершенный код. В общем в цикле изменяющая свое значение переменная раз-поз-раз с каждым проходом цикла сохраняет его из регистра процессора в память и из памяти опять загружает в регистр. А зачем ? Ведь это значение могло бы хранится в регистре все время и этот регистр ничем не занят. Да и вообще много приемов есть оптимальности. Вот например, абсолютно параллельное выполнение целочисленных и вещественных команд. Эти два типа команд выполняются разными участками кристалла процессора и эти участки кристалла могут работать параллельно, если на конвеере АЛУ будут попеременно команды целоч. и вещественные. А команды расширения АЛУ SSE2 - это же вообще рулёз. За один такт выполняются сразу четыре арифметические операции или логические. Для тех кто незнаком вкратце объясню - операнды хрянятся в 128-разрядных XMM-регистрах в упакванном виде - 4 целых 32-битных числа может поместится. Вот допустим есть пара таких регистров, или регистр и 128-битная ячейка памяти. И есть команды для параллельного выполнения операций. Ускорение - в 4 раза. Ну куда там тем языкам ЯВУ до такого... ? Хотя фирма INTEL разработала свой компилятор для C под процессоры Pentium 4, и утверждает, что он учитывает всю специфику оптимизации по Пенёк. Ну так вот вы теперь можете мне ответить - не хочешь не пиши на C#. Правильно. А какой кайф от того, что понимаешь что твоя прога будет выполняться не АЛУ напрямую, а ещё через компилятор JIT проходить ? Так же не интересно. Я в общем привык писать под проц с мыслью о том что он не будет работать как мудак. А тут ещё 2 компилера... Ну в общем, что там Anders Hejlsberg про это думает ? Как там насчет быстродействия ? Весь кайф портится, не так ли ? Ответ тут только один. Надо писать DLL на Асм и связывать их с кодом на C#. Насчет отладки Асм-кода - даже не знаю пока хорошего решения. Наверно писать надо сначала в C++ а потом переводить в C#. Что худшего в C++ ? Генерится код в пол метра минимум, даже если на форме нет ни одного контрола. А по какой причине столько кода ? есть вероятность того, что он выполнится ? Microsoft стремится сделать программы кросплатформенными. ................................................. |
| Автор: Medved 15.7.2006, 01:00 |
Дружище! Один топик - один вопрос! Не нарушайте пожалуйста правила форума. |
| Автор: mr.DUDA 15.7.2006, 11:37 |
| Автор: Medved 15.7.2006, 15:36 |
Правила форума: можно прочитать вот здесть - http://forum.vingrad.ru/index.php?s=&act=SR&f=27 |