Последнее время С и С++ являются наиболее
используемыми языками для разработки коммерческих и бизнес приложений. Эти
языки устраивают многих разработчиков, но в действительности не
обеспечивают должной продуктивности разработки. К примеру, процесс
написания приложения на С++ зачастую занимает значительно больше времени,
чем разработка эквивалентного приложения, скажем, на Visual Basic. Сейчас
существуют языки, увеличивающие продуктивность разработки за счет потери в
гибкости, которая так привычна и необходима программистам на С/С++.
Подобные решения являются весьма неудобными для разработчиков и зачастую
предлагаются значительно меньшие возможности. Также эти языки не
ориентированы на взаимодействие с появляющимися сегодня системами и очень
часто они не соответствуют существующей практике программирования для Web.
Многие разработчики хотели бы использовать современный язык, который
позволял бы писать, читать и сопровождать программы с простотой Visual
Basic и в то же время давал бы мощь и гибкость C++, обеспечивал бы доступ
ко всем функциональным возможностям системы, взаимодействовал бы с
существующими программами и легко работал бы с возникающими Web
стандартами.
Учитывая все подобные пожелания, Microsoft
разработала новый язык - C#. В него входит много полезных особенностей -
простота, объектная ориентированность, типовая защищенность, "сборка
мусора", поддержка совместимости версий и многое другое. Данные
возможности позволяют быстро и легко разрабатывать приложения, особенно
COM+ приложения и Web сервисы. При создании C#, его авторы учитывали
достижения многих других языков программирования: C++, C, Java, SmallTalk,
Delphi, Visual Basic и т.д. Надо заметить что по причине того, что C#
разрабатывался с чистого листа, у его авторов была возможность (которой
они явно воспользовались), оставить в прошлом все неудобные и неприятные
особенности (существующие, как правило, для обратной совместимости),
любого из предшествующих ему языков. В результате получился действительно
простой, удобный и современный язык, по мощности не уступающий С++, но
существенно повышающий продуктивность разработок.
Web интеграция
Ввиду своего очень удобного
объектно-ориентированного дизайна, C# является хорошим выбором для
быстрого конструирования различных компонентов - от высокоуровневой бизнес
логики до системных приложений, использующих низкоуровневый код. Также
следует отметить, что C# является и Web ориентированным - используя
простые встроенные конструкции языка ваши компоненты могут быть легко
превращены в Web сервисы, к которым можно будет обращаться из Internet
посредством любого языка на любой операционной системе. Дополнительные
возможности и преимущества перед другими языками приносит в C#
использование передовых Web технологий, таких как: HTML (Hypertext Markup
Language), XML (Extensible Markup Language ) и SOAP (Simple Object Access
Protocol). Среда разработки Web сервисов позволяет программисту смотреть
на существующие сегодня Web приложения, как на родные C# объекты, что дает
возможность разработчикам соотнести имеющиеся Web сервисы с их познаниями
в объектно-ориентированном программировании.
Исключение ошибок
Очень часто можно прослеживать такую связь - чем
более язык защищен и устойчив к ошибкам, тем меньше производительность
программ, написанных на нем. К примеру рассмотрим две крайности - очевидно
это Assembler и Java. В первом случае вы можете добиться фантастической
быстроты своей программы, но вам придется очень долго заставлять ее
работать правильно не на вашем компьютере. В случае же с Java - вы
получаете защищенность, независимость от платформы, но к сожалению,
скорость вашей программы вряд ли совместима со сложившимся представлением
о скорости, например какого-либо отдельного клиентского приложения
(конечно существуют оговорки - JIT компиляция и прочее). Рассмотрим C++ с
этой точки зрения - на мой взгляд соотношение в скорости и защищенности
близко к желаемому результату, но на основе собственного опыта
программирования я могу с уверенностью сказать, что практически всегда
лучше понести незначительную потерю в производительности программы и
приобрести такую удобную особенность, как "сборка мусора", которая не
только освобождает вас от утомительной обязанности управлять памятью
вручную, но и помогает избежать вам многих потенциальных ошибок в вашем
приложении. В действительности скоро "сборка мусора", да и любые шаги к
устранению потенциальных ошибок становятся отличительными чертами
современного языка. В C#, как в несомненно современном языке, также
существуют характерные особенности для обхода возможных ошибок. Например,
помимо упомянутой выше "сборки мусора", там все переменные автоматически
инициализируются средой и обладают типовой защищенностью, что позволяет
избежать неопределенных ситуаций в случае, если программист забудет
инициализировать переменную в объекте или попытается произвести
недопустимое преобразование типов. Также в C# были предприняты меры для
исключения ошибок при обновлении программного обеспечения. Изменение кода,
в такой ситуации, может непредсказуемо изменить суть самой программы.
Чтобы помочь разработчикам бороться с этой проблемой C# включает в себя
поддержку совместимости версий (vesioning). В частности, в отличии от C++
и Java, если метод класса был изменен это должно быть специально
оговорено. Это позволяет обойти ошибки в коде и обеспечить гибкую
совместимость версий. Также новой особенностью является native поддержка
интерфейсов и наследования интерфейсов. Данные возможности позволяют
разрабатывать сложные системы и развивать их со временем.
Особенности и примеры использования
Переходя к более подробному знакомству с C#,
традиционно рассмотрим программу "Hello, world":
using System;
class Hello{
static void Main() {
Console.WriteLine("hello, world");
}
} | Поместите эту программу в файл hello.cs и
скомпилируйте ее командой csc hello.cs В результате вы получите файл hello.exe,
запустив который, вы увидите надпись "hello, world".
В C# была унифицирована система типов, теперь вы
можете рассматривать каждый тип как объект. Несмотря на то, используете вы
класс, структуру, массив или встроенный тип, вы можете обращаться к нему
как к объекту. Объекты собраны в пространства имен (namespaces), которые
позволяют программно обращаться к чему-либо. Это значит что вместо списка
включаемых файлов заголовков в своей программе вы должны написать какие
пространства имен, для доступа к объектам и классам внутри них, вы хотите
использовать. В C# выражение using позволяет вам не писать каждый раз
название пространства имен, когда вы используете класс из него. Например,
пространство имен System содержит несколько классов, в том числе и
Console. И вы можете писать либо название пространства имен перед каждым
обращением к классу либо использовать using как это было показано в
примере выше. Статический метод Main - точка входа в программу. Этот
метод обязательно должен быть статическим. Далее в нашем примере
используется метод WriteLine из класса System.Console. Этот метод
отправляет строку на стандартный вывод.
Простота использования
Важной и отличительной от С++ особенностью C#
является его простота. К примеру, всегда ли вы помните, когда пишите на
С++, где нужно использовать "->", где "::", а где "."? Даже если нет,
то компилятор всегда поправляет вас в случае ошибки. Это говорит лишь о
том, что в действительности можно обойтись только одним оператором, а
компилятор сам будет распознавать его значение. Так в C#, оператор "->"
используется очень ограничено (в unsafe блоках, о которых речь пойдет
ниже), оператор "::" вообще не существует. Практически всегда вы
используете только оператор "." и вам больше не нужно стоять перед
выбором. Еще один пример. При написании программ на C/С++ вам
приходилось думать не только о типах данных но о их размере в конкретной
реализации. В C# все упрощено - теперь символ Unicode называется просто
char (а не wchar_t, как в С++) и 64-битное целое теперь - long (а не
__int64). Также в C# нет знаковых и беззнаковых символьных типов. В
C#, также как и в Visual Basic после каждого выражения case в блоке switch
подразумевается break. И более не будет происходить странных вещей если вы
забыли поставить этот break. Однако если вы действительно хотите чтобы
после одного выражения case программа перешла к следующему вы можете
переписать свою программу с использованием, например, оператора goto.
Еще одно упрощение - в C# не существует множественного наследования
классов. В действительности его использование является вовсе не самой
простой задачей и зачастую приводит к ошибкам. Вместо этого в C# принята
полная поддержка концепций COM+, которые исключают необходимость
использования множественного наследования. Многим программистам (на
тот момент, наверное, будущим программистам) было не так легко во время
изучения C++ полностью освоиться с механизмом ссылок и указателей. Многие
путались в использовании операторов "*" и "&". В C# (кто-то сейчас
вспомнит о Java) нет указателей. В действительности нетривиальность
указателей соответствовала их полезности. Например, порой, трудно себе
представить программирование без указателей на функции. В соответствии с
этим в C# присутствуют Delegates - как прямой аналог указателя на функцию,
но их отличает типовая защищенность, безопасность и полное соответствие
концепциям объектно-ориентированного программирования . Рассмотрим пример
их использования:
delegate void SimpleDelegate(); /* здесь мы объявляем тип Delegate -
принимаемые значения и возвращаемый тип */
class Test {
static void F() {
System.Console.WriteLine("Test.F");
}
static void Main() {
SimpleDelegate d = new SimpleDelegate(F); /* создание объекта -
Delegate */
d(); // Запуск Delegate
}
} |
Delegates это объекты, производные от общего
базового объекта System.Delegate. Этот тип являет собой некую сущность,
которую можно вызывать. В случае с экземпляром объекта - эта сущность
состоит из экземпляра и его метода, в случае статического метода - из
класса и статического метода этого класса. Именно благодаря этому не
нарушатся концепции объектно-ориентированного программирования, а именно
не используются методы объекта в отрыве от самого объекта.
Типовая защищенность
Вы можете не согласиться, но на мой взгляд
типовая защищенность способствует написанию устойчивых программ. К
примеру, в C# все динамические объекты и массивы автоматически
инициализируются нулем. Несмотря на то, что C# автоматически не
инициализирует локальные переменные, компилятор выдаст вам предупреждение,
если вы попытаетесь использовать их до инициализации. Также, при обращению
к массиву, выполняется проверка на выход за его границы. Таким образом в
C#, в отличие от С++, невозможно изменить не отведенную для вас память.
В C# вы не можете создать ссылку на произвольную область памяти. Все
приведения типов должны быть безопасными. К примеру, вы не можете
использовать приведение между reference (обращение по указателю на область
памяти) и value (обращение к значению) типами. "Сбор мусора" обеспечит
отсутствие в вашем коде болтающихся впустую ссылок. Типовая
защищенность сильно связана с контролем на переполнение. Не допускаются
арифметические операции, которые будут переполнять допустимое значение
переменной или объекта. Хотя, конечно, существует лишь неполный набор
факторов, которые говорят о явном переполнении переменной. Если же вам не
нравится такая проверка - вы можете ее отменить. Вообще, типы данных,
присутствующие в C#, отличаются от типов в C++. Так тип char тут
16-битный, а весьма полезные типы string и decimal являются встроенными.
Но куда большие отличия мы можем увидеть при рассмотрении массивов. В C#
могут быть многомерные массивы (multidimentional) и неровные (или
неуточненные - jagged - массивы массивов):
int[ ] intArray; // Просто массив
int[ , , ] intArray; // Трехмерный массив
int[ ][ ] intArray; // неровный массив массивов
int[ ][ , , ][ , ] intArray; /* Одномерный массив трехмерных массивов
двумерных массивов :) */ |
В принципе, использование массивов ничего нового
из себя не представляет, но было бы полезно рассмотреть пример
инициализации неровного массива:
int[][] intArray = new int[][] {
new int[] {2,3,4}, new int[] {5,6,7}
}; |
В C# появился оператор циклов foreach - перебор
всех элементов "списка". Его использование зависит от типов объектов, к
которым он применяется, по причине того, что понятие "списка" может быть
определено по разному. Проиллюстрируем его действие на примере:
int[] intArray = {2, 4, 6, 8, 10, -2, -3, -4, 8};
foreach (int i in intArray) {
System.Console.WriteLine(i); // Вывод элементов массива по порядку
} |
Удобство и современность C#
Хотелось бы подчеркнуть современное удобство C#.
Когда вы начнете работу с C#, а надеюсь это произойдет как можно скорее,
вы увидите, что довольно большое значение в нем имеют пространства имен.
Уже сейчас, на основе первого примера, вы можете судить об этом - ведь все
файлы заголовков заменены именно пространством имен. Так в C#, помимо
просто выражения using, предоставляется еще одна очень удобная возможность
- использование дополнительного имени (alias) пространства имен или
класса. Следующие примеры продемонстрируют это:
namespace NS3 { /* предположим, что у нас имеется вложенное пространство имен
NS1.NS2, в котором находится ClassA */
using A = NS1.NS2.ClassA; // Определяем дополнительное имя для класса
class ClassB: A {} // Используем его
}
namespace NS3 { // или на уровне пространств имен
using C = NS1.NS2; // Определяем дополнительное имя для пространства имен
class ClassB: C.ClassA {} // Используем его
} |
Современность C# проявляется и в новых шагах к
облегчению процесса отладки программы. Традиционым средством для отладки
программ на стадии разработки в C++ является маркировка обширных частей
кода директивами #ifdef и т.д. В C#, используя атрибуты, ориентированные
на условные слова, вы можете куда быстрее писать отлаживаемый код. Покажем
это на примере:
<Код из первого файла>
using System;
namespace DotSiteRes {
public class HelloWorld {
[conditional("DEBUG")] /* последующий метод будет выполняться
только в случае определения DEBUG */
public static void SayHi() {
Console.WriteLine("Hello, World!");
return;
}
...
}
}
<Код из второго файла>
using System;
using DotSiteRes;
#define DEBUG
class CallingDotSiteRes {
public static void Main(string[] args) {
HelloWorld.SayHi();
return;
}
} |
В данном случае будет выведена строка "Hello,
world". Если бы DEBUG не было определено, ничего бы не произошло. Не менее
полезное использование атрибутов будет продемонстрировано далее.
В наше время, когда усиливается связь между миром
коммерции и миром разработки программного обеспечения, и корпорации тратят
много усилий на планирование бизнеса, ощущается необходимость в
соответствии абстрактных бизнес процессов их программным реализациям. К
сожалению, большинство языков реально не имеют прямого пути для связи
бизнес логики и кода. Например, сегодня многие программисты комментируют
свои программы для объяснения того, какие классы реализуют какой-либо
абстрактный бизнес объект. C# позволяет использовать типизированные,
расширяемые метаданные, которые могут быть прикреплены к объекту.
Архитектурой проекта могут определяться локальные атрибуты, которые будут
связанны с любыми элементами языка - классами, интерфейсами и т.д.
Разработчик может программно проверить атрибуты какого-либо элемента. Это
существенно упрощает работу, к примеру, вместо того чтобы писать
автоматизированный инструмент, который будет проверять каждый класс или
интерфейс, на то, является ли он действительно частью абстрактного бизнесс
объекта, можно просто воспользоваться сообщениями основанными на
определенных в объекте локальных аттрибутах. Следующий пример иллюстрирует
это:
using System;
[AttributeUsage(AttributeTargets.All)]
/* Здесь уже пример использования аттрибутов - с их помощью мы говорим
где может применяться наш атрибут-класс, определяемый ниже */
public class HelpAttribute: Attribute {
/*аттрибут-класс - наследник класса System.Attribute. Необходимо
заметить, что в HelpAttribute, Help является непосредственным именем
аттрибут-класса, а Attribute - суффиксом. При использовании
аттрибут-класcа HelpAttribute вы сможете обращаться к нему коротко -
просто Help. */
public HelpAttribute(string url) {
this.url = url;
}
public string Topic = null;
private string url;
public string Url {
get { return url; }
}
}
[Help("http://www.mycompany.com/:/Class1.htm")] /* Применение нашего аттрибута
к новому классу */
public class Class1 {
[Help("http://www.mycompany.com/:/Class1.htm", Topic = "F")]
/* Применение нашего аттрибута к методу */
public void F() {}
}
class Test { // Класс, проверяющий аттрибуты
static void Main() {
Type type = typeof(Class1);
object[] arr = type.GetCustomAttributes(typeof(HelpAttribute));
/* прочтение аттрибутов класса Class1 */
if (arr.Length == 0)
Console.WriteLine("Class1 has no Help attribute.");
else {
HelpAttribute ha = (HelpAttribute) arr[0];
// Вывод аттрибутов
Console.WriteLine("Url = {0}, Topic = {1}", ha.Url, ha.Topic);
}
}
} |
Дополнительные возможности
Реальный опыт разработки говорит нам о том, что
некоторым программам и по сей день требуется native код, либо из
соображений производительности, либо для взаимодействия с существующими
API. Эта причина может заставить разработчиков использовать С++ даже когда
они предпочли бы более продуктивную среду разработки. В С# для решения
этой проблемы были приняты несколько решений: включена native поддержка
COM-технологии и WinAPI, допускается ограниченно использовать native
указатели. Разработчику больше не нужно точно реализовывать IUnknown и
другие COM интерфейсы. Теперь эти особенности являются встроенными. Также
C# программы могут органично использовать существующие COM объекты
независимо от того, на каком языке они были написаны. Для тех
разработчиков которым это необходимо, в C# была включена специальная
особенность, которая позволяет программе обращаться к любому существующему
native API. Внутри специально помеченного блока (unsafe) программисту
позволяется использовать указатели и традиционные особенности С/С++, такие
как, управление памятью вручную и арифметика указателей. Этот большой плюс
отличает C# от других сред разработки. Вместо того, чтобы отказаться от
уже существующего С/С++ кода, вы можете разрабатывать C# приложения
используя его. Ниже демонстрируется использование unsafe блоков:
struct Point{
public int x;
public int y;
public override string ToString() {
return "(" + x + "," + y + ")";
}
}
class Test {
unsafe static void Main() {
Point point;
Point* p = &point;
p->x = 10;
p->y = 20;
Console.WriteLine(p->ToString());
}
} |
Как вы увидели, внутри unsafe блоков, как и в
C++, возможно использование указателей. Также допускается и использование
оператора "->", который может быть заменен "(*p).".
Заключение
В заключение, я хотел бы поздравить вас - теперь
вы имеете представление о C# - языке следующего поколения. В данной статье
было рассказано о многих его возможностях, но смею вас уверить - далеко не
всех. Надеюсь, к этому моменту вы уже обладаете желанием использовать C# в
своих разработках. Мы планируем выпустить цикл статей и документации,
которые вы очень скоро увидите на этом сайте. Обратившись к ним, вы
сможете ознакомиться поближе с C# и примерами его использования в самых
разных сферах.
31 октября этого года C# был стандартизован.
Сейчас доступна его спецификация на английском языке, вы можете найти ее
на сайте Microsoft. Там же вы можете скачать Microsoft Visual Studio .NET
beta 1 и уже сегодня начать работу с C# и Microsoft .NET framework.
Желаю удачи :)
Уткин Алексей
(dotSITE Team)
|