![]() |
|
Модераторы: Partizan, gambit |
![]()
|
|
| Medved |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
Язык программирования Си#: критическая оценка
Сергей Свердлов ОГЛАВЛЕНИЕ Избыточность Побочный эффект Тяжеловесность Читать или писать? Перспективы Си# Избыточность Некоторые средства языка Си# являются избыточными — в том смысле, что не добавляют к языку никаких функциональных возможностей, а лишь позволяют в иной форме записать то, что и так может быть выражено достаточно просто. К числу таких средств относятся свойства (properties) и индексаторы (indexers). Свойства маскируют обращение к процедуре (методу) под обращение к переменной (полю). Это широко применяется в системах визуального программирования, когда изменение значения поля, определяющего внешний вид какого-либо экранного элемента, должно сопровождаться перерисовкой самого элемента. При использовании свойств для выполнения обоих этих действий достаточно свойству присвоить значение. Пусть строковое (типа string) свойство Title экранных объектов класса Element задает надпись-заголовок таких объектов. Объявление этого свойства может выглядеть так: public class Element { string title; // Это поле public string Title { // А это - свойство get { return title;} set { title = value; Repaint(); // Перерисовать } } // Далее определяются другие поля и методы ... } Сама надпись хранится в поле title, а свойство Title организует доступ к нему c помощью подпрограмм get (получить) и set (установить). Если e — переменная, обозначающая объект класса Element, то для изменения надписи и перерисовки объекта на экране достаточно записать: e.Title = “Привет”; Такое присваивание приводит к выполнению подпрограммы set. А, например, при выполнении оператора, добавляющего к заголовку восклицательный знак: e.Title += “!”; неявно вызываются и get, и set. Между тем доступ к заголовку экранного элемента может быть оформлен и без использования свойств: public class Element { string title; public string getTitle() { return title; } public void setTitle( string value ) { title = value; Repaint(); // Перерисовать } // Далее определЯютсЯ другие полЯ и методы ... } В этом случае вместо e.Title = “Привет”; нужно записать e.setTitle(“Привет”); а взамен e.Title += “!”; необходимо использовать e.setTitle(e.getTitle()+“!”); Конструкции получаются чуть более громоздкими, но зато они не вводят в заблуждение читателя программы. От него не скрывается тот факт, что изменение заголовка выполняется подпрограммой, которая кроме присваивания значения может выполнить и другие действия. В первом же случае по тексту программы невозможно (без обращения к описанию класса Element) отличить использование свойств от использования обычных полей. Программирование в среде визуальных систем, подобных Delphi, откуда в язык Си# перекочевали свойства, можно разделить на несколько уровней. Первый — разработка прикладных программ. В этом случае программист, создавая программу с графическим интерфейсом пользователя, как правило, оперирует лишь готовыми компонентами с уже запрограммированными свойствами. Внешняя неотличимость свойств и полей в этом случае не слишком мешает, поскольку речь идет о применении уже отлаженных библиотек, а в распоряжении программиста есть встроенная система помощи. Более того, многие свойства визуальных компонентов даже не фигурируют в том случае, если программа пишется вручную. Значения свойств окон, кнопок и других элементов интерфейса просто задаются в режиме диалога с визуальной системой, и прикладной программист в этой среде по сути имеет дело не со всем языком программирования, а лишь с той его частью, в которую входит возможность использования готовых свойств готовых классов, но не входит их определение. Второй уровень — разработка визуальных компонентов. Здесь на программиста возлагается полная ответственность за корректность создаваемой системы классов. Наконец, следует рассмотреть использование языка, оснащенного свойствами, вне контекста визуальной среды*. В этой ситуации, то есть в общем случае, когда свойства применяются не только для визуальных элементов, неотличимость вызова подпрограмм (get и set) от обращения к полю может сослужить плохую службу, снизив возможность понимания программы. По ее тексту уже нельзя понять, сводится ли действие лишь к изменению или получению значения поля, либо оно сопряжено с выполнением и других операций. Весьма неоднозначный смысл имеет конструкция Си#, называемая индексатором. Индексаторы — это элементы классов (а также интерфейсов и структур), позволяющие обращаться к объектам как к массивам (указанием индекса в квадратных скобках). Реализуется такой доступ неявно вызываемыми подпрограммами get и set. Например, обращение к отдельным битам 32-разрядного целого значения (bits) можно замаскировать под обращение к логическому массиву. Для этого создается такое описание (в данном случае структуры): public struct BitSet { int bits; // Поле целого типа public bool this[int i] { // Индексатор get{return 0<=i && i<32? (bits & 1< set { if(i<0 || i>31) return; if(value) bits |= 1< } } } Теперь использование переменной типа BitSet может выглядеть, например, так: BitSet b = new BitSet(); // Описание переменной ... // Все биты устанавливаются в единичное значение for (int i=0; i<32; i++ ) b[i] = true; ... if(b[i]) ( // Проверка i-го бита ... В описании типа BitSet использовано много специфических обозначений: && — условное “И”; & — поразрядное логическое “И”; << — сдвиг влево; != — не равно; ... ? ... : ... — условная операция, если логическое выражение перед “?” истинно, то результат операции вычисляется по выражению, записанному перед двоеточием, если нет — по выражению, записанному после двоеточия; || — поразрядное логическое “ИЛИ”; |= — присваивание с поразрядным “ИЛИ” (обратите внимание на схожесть с !=); &= — присваивание с поразрядным “И”; ~ — поразрядное “НЕ”. Идентификатор value представляет значение, переданное подпрограмме set. Конечно, те же функциональные возможности могут быть обеспечены и без индексаторов: public struct BitSet { int bits; public bool get(int i) { // Получить return 0<=i && i<32? (bits & 1< } public void set(int i, bool value) { // Установить if(i<0 || i>31) return; if(value) bits |= 1< } } Обратите внимание, что содержание подпрограмм get и set нисколько не изменилось. Только теперь это обычные методы. Для доступа к отдельным битам квадратные скобки уже не применить: BitSet b = new BitSet(); // Описание переменной ... // Все биты устанавливаются в единичное значение for (int i=0; i<32; i++ ) b.set(i, true); ... if(b.get(i)) ( // Проверка i-го бита ... По-видимому, одной из причин появления индексаторов в Си# было желание создателей языка естественным образом оформить обращение к символам строк, которые в Си# являются объектами, но не массивами. Если s — строка (string), то только благодаря наличию в классе string индексатора i-й символ строки s можно обозначить s[i]. В языке Java, где строки — тоже объекты, для обращения к отдельному символу приходится вызывать специальный метод: s.charAt(i). Каков же баланс выгод и потерь от введения в язык свойств и индексаторов? Обсуждая этот вопрос, будем помнить, что ни те, ни другие не добавляют никаких принципиально новых возможностей. Преимущество обнаруживается одно: упрощение нотации. Действия по доступу к объекту и его изменению записываются как обращение к полю в случае использования свойств и как обращение к элементу массива при использовании индексаторов. Недостатков можно назвать больше: * усложняются язык и компилятор. С появлением свойств и индексаторов возникает много новых правил. В справочнике по Си# свойствам посвящено целых восемь страниц; * при использовании свойств и индексаторов от программиста скрываются затраты на обработку подпрограмм доступа get и set, что провоцирует к употреблению неадекватных и неэффективных приемов. Например, использование индексатора для доступа к элементам линейного списка по их номеру** при значительной длине списка гораздо менее эффективно, чем обращение к элементу массива, хотя выглядит так же. Применение такого индексатора для последовательного просмотра списка — и вовсе абсурд, к которому, тем не менее, вас подталкивают; * ухудшаются возможности понимания программы. По ее тексту уже нельзя узнать, имеем ли мы дело с полем или свойством, массивом или индексатором. Это при том, что обращение к полю всегда связано лишь с получением или установкой его значения, а обращение к свойству может предусматривать выполнение любых действий. Аналогично обстоит дело и с массивами и индексаторами. Приведу пример, когда простейший фрагмент, работа которого в иных обстоятельствах понималась бы абсолютно однозначно, а при использовании Си# совершенно непредсказуема: int i, s=0; for (i=1; i<=100; i++ ) a[i]=i; for (i=1; i<=100; i++ ) s += a[i]; System.Console.WriteLine(s); Ну и что тут такого? Вначале элементам “a” поочередно присваиваются значения первых ста чисел натурального ряда: 1, 2, 3, ..., 99, 100. Затем вычисляется и выводится сумма этих чисел, которая обязательно должна быть равна 5050. Но ничего подобного! Напечатанное этой программой значение может оказаться каким угодно. Например, равным 338 350, если “a” — индексируемый объект такого типа: class Array { public int this[int i]{ get{ return i*i; } // i-е значение равно i*i set {} // Никаких действий } } * Для языка Объектный Паскаль, который используется в Delphi, такое применение не слишком актуально. Это специфический язык конкретной системы визуального программирования. Однако язык Си#, по-видимому, претендует на всеобщность, на существование не только в рамках конкретной среды. ** Именно этим примером иллюстрируется использование индексаторов в документации по Си#. Источник: http://www.pcweek.ru/?ID=56351 -------------------- |
|||
|
||||
| arilou |
|
||||||
![]() Великий МунаБудвин ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 2646 Регистрация: 15.7.2004 Где: город-герой Минск Репутация: 21 Всего: 61 |
Ну и бредятина, честно говоря. Свойства для того и сделаны (помимо всего прочего), чтобы дать возможность программисту внести логику на присваивание значения, и на его получение - простой пример - Lazy Load. И слова о том, что это ухудшает читабельность программы, по меньшей мере, спорны.
Еще один маразм. А как насчет обращения к hashtable по ключу? Человек явно не понимает, или, что более вероятно, не видит в силу закоснелости, удобных нововведений.
Приводить пример использования без объявления (до самого примера) типа переменной "a" - очень интересный подход. Может, он и программы так же пишет? В общем, ерунда какая-то. Статья пуриста от программирования. Заметил еще вот дату публикации - я думаю, что на такое старье тут в форуме не стоит даже ссылаться - с тех пор уже многое было сделано. Это сообщение отредактировал(а) arilou - 21.12.2004, 12:40 |
||||||
|
|||||||
| Medved |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
Ок. Интересно было услышать мнение учатсников. Собственно для этого и постил.
-------------------- |
|||
|
||||
| Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: нет Всего: 317 |
Привык к полям(гетеры/сетеры) и индексному обращению из JS, для меня это привычно и удобно(индексы). Замаскированные Гетеры/сетеры действительно могут вести к неясному и не эффективному коду. Но как говорится не можешь держать ружьё - не ходи на охоту. Количество ошибок зависит от програмиста.
-------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
| arilou |
|
|||
![]() Великий МунаБудвин ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 2646 Регистрация: 15.7.2004 Где: город-герой Минск Репутация: 21 Всего: 61 |
Хорошо сказано |
|||
|
||||
| bel_nikita |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Эксперт Сообщений: 2304 Регистрация: 12.10.2003 Где: Поезд №21/22 ( ст . Прага ) Репутация: нет Всего: 47 |
Туфта полная.
Неужили это и в самом деле в журнале печаталось? Что-то автор, похоже, не очень в программировании... |
|||
|
||||
| Medved |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
Я согласен - что статья плод закостенелого программиста, привыкшего с привычному. Судит только со своей колокольни.
-------------------- |
|||
|
||||
| bel_nikita |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Эксперт Сообщений: 2304 Регистрация: 12.10.2003 Где: Поезд №21/22 ( ст . Прага ) Репутация: нет Всего: 47 |
Я бы сказал по-другому: Плохому танцору - известно, что мешает
|
|||
|
||||
| Medved |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
После дельфи я от этой возможности просто тащюсь! -------------------- |
|||
|
||||
| DENNN |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 3878 Регистрация: 27.3.2002 Где: Москва Репутация: нет Всего: 43 |
Я согласен с той мыслью в статье, что маскирование под свойствами вызова функций может приводить к неоднозначности в сложных проектах. Гораздо надежней всегда четко понимать, что происходит: вызывается функция или изменяется значение переменной.
Перегрузка же операторв хороша в тех случаях, для которых она изадумывалась. Например для класса вектора перегрузить операцию умножения и скалярного умножения. Перегружать же операции там где это не требуется - ИМХО, плохой стиль программирования. |
|||
|
||||
| Medved |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
Ну не знаю. Спорно. Возникают порой такие случаи, например, когда мне необходимо выполнить сложение полей данных, n-го количества экземпляров одного пользовательского класса.
Тоже спорно. Просто надо разумно использовать префиксы и давать нормальные названия пользовательским элементам. Добавлено @ 14:10 Коментарии к статье почитайте: http://www.pcweek.ru/?ID=56351&Discuss=1&M...#Message_108747 -------------------- |
||||
|
|||||
| Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: нет Всего: 317 |
На мой взглят перегузка операторов это одна из самых больших ошибок, которые когда либо придумывали.
Никогда не давал переменным префиксы, меня добивает когда тип переменной ты узнаешь по префиксу в имени. Считаю префискы в именах пережитком прошлого, IMXO. -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
| Medved |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 7209 Регистрация: 15.9.2002 Где: Kazakhstan, Astan a Репутация: 3 Всего: 154 |
ИМХО это поверхностное суждение и в корне неправильное, я бы даже сказал непрофессиональное. Весь мой опыт реального программирования (и не только мой) говорит о том что это только улучшает читабельность кода. А это в свою очередь приводит к меньшему количеству ошибок. Не обжайся пожалуйста на мои слова, докажи реальными примерами, что я не прав. А голословно оговорить что-то, все могут. -------------------- |
||||
|
|||||
| bel_nikita |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Эксперт Сообщений: 2304 Регистрация: 12.10.2003 Где: Поезд №21/22 ( ст . Прага ) Репутация: нет Всего: 47 |
|
||||
|
|||||
| Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: нет Всего: 317 |
Ну моё утверждение тоже было мое ИМХО -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
![]()
|
| Прежде чем создать тему, посмотрите сюда: | |
|
|
Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов. Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :) Так же не забывайте отмечать свой вопрос решенным, если он таковым является :) Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |