Для начала рассмотрим различия в структуре программы, написанной на C#, от программы, написанной на C++, а потом перейдем к детальному рассмотрению особенностей языка C#.
C++ | C# |
#include <iostream> #include <tchar.h> using namespace std; struct S { private: int a; int b; public: S(){a=0;b=0;} int Sum(){return a++ + ++b;} }; int _tmain(int argc, _TCHAR* argv[]) { S a; for(int i=0; i<10;i++) { cout << a.Sum() << endl; } return 0; } | using System; using cout = System.Console; using d3d = Microsoft.DirectX.Direct3D; namespace ConsoleApplication1 { public struct S { private int a;// Поля можно не инициализировать private int b;/* По умолчанию поля int равны 0*/ public int Sum() { return (a++ + ++b);} } /** * <summary> * Это ХML комментарий * </summary> * */ class Class1 { /// <summary> /// Это XML комментарий, описывающий метод Main /// </summary> /// <param name="args">Комментарий для /// параметров метода</param> static void Main(string[] args) { S a = new S(); for(int i = 0; i<10; i++) { System.Console.WriteLine(“Вот.”); Console.WriteLine("{0}",a.Sum()); cout.WriteLine("alias"); } } } } |
Классы в С#
С# похож на язык Java в том отношении, что он требует, чтобы вся программная логика была заключена в определения типов (под типом подразумеваются классы, интерфейсы, структуры и аналогичные компоненты языка). В отличие от С (и С++) глобальные функции и глобальные переменные в чистом виде в С# использовать нельзя. Простейший класс С# может быть определен следующим образом:
// Обычно для файлов классов C# используется расширение *.cs
using System;
class HelloClass
{
public static int Main (string[] args)
{
Console.WriteLine ("Hello World!");
return 0;
}
}
Мы создали класс HelloClass, который поддерживает единственный метод – Main(). В любом приложении С# должен быть определен класс с методом Маin(). Этот метод будет использован как точка входа вашего приложения - то есть работа приложения начнется с выполнения этого метода. В принципе, возможно создать несколько классов с методами Маin(), однако в этом случае придется явно указать, какой из этих методов будет использован в качестве точки входа. Если вы пропустите такое указание, результатом станет сообщение компилятора об ошибке.
Обратите внимание на то, что первая буква в названии метода Main() - заглавная. Такое написание обязательно.
В нашем случае метод Main() был определен как public и как static. Ключевое слово public в определении метода означает, что этот метод будет доступен извне, а ключевое слово static говорит о том, что этот метод позиционируется на уровне класса, а не отдельного объекта и будет доступен даже тогда, когда еще не создано ни одного экземпляра объекта данного класса.
Кроме того, наш метод Маin() принимает единственный параметр, который должен быть набором, символов (string[] аrgs). Его можно использовать для приема параметров командной строки при запуске приложения.
Вся программная логика HelloClass заключена в самом методе Маin(). Этот метод обращается к классу Console, определенному внутри пространства имен System. Один из членов метода Console - статический метод WriteLine(), который можно использовать для вывода строки на системную консоль.
Обработка параметров командной строки
Предположим, что вы хотите изменить класс HelloClass таким образом, чтобы он не игнорировал параметры командной строки, а обрабатывал их. После внесения необходимых изменений HelloClass может выглядеть так:
using System;
class HelloClass
{
public static int Main (string[] args)
{
// Выводим пара метры на консоль!
for(int х=0; х < args.Length; х++)
{
Console.WriteLine("Arg: {0}". args[x]):
}
Console.WriteLine("Hel1o, World!");
return 0;
}
}
В нашей программе производится проверка, были ли вообще переданы какие-либо значения в качестве параметров командной строки. Такая проверка производится с помощью свойства Length объекта System.Array. Если в массиве есть по крайней мере один член (параметр командной строки), запускается цикл, в ходе которого на консоль выводятся все параметры командной строки с первого до последнего.
Цикл для последовательной обработки всех параметров командной строки можно организовать и другим способом, использовав конструкцию foreach:
foreach (string s in args)
Conso1е.WriteLine( "Arg: {0}", s);
Создание объектов: конструкторы
Во всех объектно-ориентированных языках четко различаются понятия «класс» и «объект». Класс - это определяемый пользователем тип данных (user-defined type, UDT). Класс можно представить себе как чертеж, по которому организуются его члены. В отличие от класса объектом называется конкретный экземпляр определенного класса, с помощью которого обычно и производятся определенные действия в программе.
Единственный способ создания нового объекта в С# - использовать ключевое слово new:
HelloClass сl = new HelloC1ass():
Ключевое слово new означает, что среде выполнения следует выделить необходимое количество оперативной памяти под экземпляр создаваемого объекта. Выделение памяти производится из «кучи», находящейся в распоряжении среды выполнения .NET (managed heap управляемой кучи). В С# переменные класса - это ссылки на объект в памяти, но не сами объекты.
В нашем примере объекты создаются при помощи консmрукmора по умолчанию. Компилятор С# автоматически снабжает конструктором по умолчанию любой класс. Если есть необходимость, вы можете переопределить этот конструктор, заставив его выполнять нужные вам действия. Как и в С++, конструктор класса по умолчанию - это конструктор, который не принимает никаких параметров. Но в С++ при создании объекта при помощи конструктора по умолчанию переменные остаются неинициализированными. В С# конструктор по умолчанию присваивают всем данным состояния значения по умолчанию – в большинства случаев 0.
Часто класс помимо конструктора по умолчанию снабжается и другими конструкторами - принимающими параметры. Использование такого конструктора - самый простой способ инициализировать состояние объекта в момент его создания, установив нужные вам значения. В качестве примера мы еще раз обратимся к классу HelloClass. При этом мы придадим этому классу несколько переменных-членов, значения которых можно настраивать, конструктор с параметрами, а также переопределим конструктор по умолчанию - конструктор без параметров.
c1ass He11oClass
{
publiс HelloClass(){...} // Конструктор по умолчанию
public HelloClass(int х, int у){...} // Наш собственный конструктор
}
Ввод и вывод с использованием класса Console
В большинстве созданных нами приложений использовался класс System.Consolе – один из многих классов, определенных внутри пространства имен System.
Главные методы класса Console - это методы ReadLine() и WriteLine (оба этих метода определены как статические). WriteLime() выводит символьную строку (дополняя ее в конце символами перехода на новую строку и возврата каретки) на системную консоль. Метод Write() делает то же самое, но уже без дополнения символами перехода на новую строку. Метод ReadLine() позволяет считать информацию с системной консоли до ближайшего символа перехода на новую строку, метод Read() считывает с системной консоли единственный символ.
Средства форматирования строк в С#
Начиная с самых первых примеров, вы постоянно встречали в коде одни и те же метки {0}, {1} и т. д. В С# предусмотрены новые средства форматирования символьных строк, отчасти напоминающие старую добрую функцию С printf(), однако без загадочных флагов %d, %s, %с и остальных. Применение этих меток проще проиллюстрировать еще одним примером:
Console.WriteLine("Int is: {0}\nFloat is: {l}\nYou are: {2}", theInt, theFloat. myIO.ТоStriпg());
Первый параметр, передаваемый WriteLine(), представляет собой строку форматирования с подстановочными выражениями {0}, {1}, {2} и т. п. Остальные параметры WriteLine() - это как раз те значения, которые последовательно подставляются в места, обозначенные подстановочными выражениями. Создатели библиотеки базовых типов позаботились о перегрузке метода WriteLine() таким образом, что в качестве второго параметра этого метода можно передавать массив объектов. При этом подстановочные выражения будут указывать на элементы этого массива:
object[] stuff = { "Hello", 1, "There", "83", 99.99933 };
Console.WriteLine("The Stuff: {0}, {1}, {2}, {3}, {4}", stuff);
В каждом подстановочном выражении при желании можно использовать параметры форматирования, представленные в табл.1 (эти параметры можно записывать как строчными, так и прописными буквами).
Таблица 1. Параметры форматирования С#
Параметр | Значение |
С или с | Используется для вывода значений в денежном (currency) формате. По умолчанию перед выводимым значением подставляется символ доллара ($), хотя можно отменить подстановку этого символа при помощи объекта NumberFormatInfo |
D или d | Используется для вывода десятичных значений. После этого символа можно указать количество выводимых символов после запятой |
Е или е | Для вывода значений в экспоненциальном формате |
F или f | Вывод значений фиксированной точностью |
G или g | Общий (general) формат. Применяется для вывода значений с фиксированной точностью или в экспоненциальном формате |
N или n | Стандартное числовое форматирование с использованием разделителей (запятых) между разрядами |
X или x | Вывод значений в шестнадцатеричном формате. Если вы использовали прописную Х, то буквенные символы в шестнадцатеричных символах также будут прописными |
Символы форматирования следуют в подстановочных выражениях сразу же за номером подставляемого параметра через двоеточие: {0:С}, {1:d}, {2:Х} и т. д.
Структурные и ссылочные типы
Все типы в С# разделяются на две основные разновидности: структурные типы (value-based) и ссылочные типы (reference-based). К структурными типам относятся все числовые типы данных (int, float и пр.), а также перечисления и структуры. Память для структурных типов выделяется из стека. При присвоении одного структурного типа другому присваивается не сам тип (как область в памяти), а его побитовая копия.
struct FOO
{public int x,у;}
//Теперь мы создадим структуру
FOO f1 = new FOO();
f1.x = 100;
f2.у = 100;
//а теперь присвоим новому типу F00 (f2) существующий тип F00 (f1):
FOO f2=f1;
То если мы теперь внесем изменения к примеру в f2.x, они не отразятся на f1.x.
Ссылочные типы ведут себя совершенно по-другому. Память для них выделяется не в стеке, а в области управляемой кучи. При копировании ссылочного типа создается еще одна ссылка, которая указывает на ту же область оперативной памяти.
Точка отсчета для любых типов: System.Object
В С# все типы данных (как структурные, так и ссылочные) производятся от единого общего предка: класса System.Object. Класс System.Object определяет общее полиморфическое поведение для всех типов данных в .NET. Во всех предыдущих примерах у нас не было необходимости явно указывать класс System.Object в качестве базового - это подразумевается само собой. Однако нам ничто не мешает сделать это, явно указав, что наш класс производится от Systeт.Object:
сlass НеlloClass : System.Object
{…}
Как и в любом другом классе C#, с классе System.Object существует свой набор членов.
Таблица 2. Главные методы объекта System.Object
Метод | Назначение |
Equals() | По умолчанию этот метод возвращает «истинно» только тогда, когда сравниваемые сущности указывают на одну и ту же область в оперативной памяти. Поэтому этот метод в его исходной реализации предназначен только для сравнения объектов ссылочных типов, но не структурных. Для нормальной работы с объектами структурных типов этот метод необходимо заместить. Однако помните, что если вы замещаете этот метод, вам потребуется также заместить метод GetНashCode() |
GetHashCode() | Возвращает целочисленное значение, идентифицирующее конкретный экземпляр объекта данного типа |
GetType() | Метод возвращает объект Туре(), полностью описывающий тот объект, из которого метод был вызван. Это метод идентификации времени выполнения (Runtime Туре Identification, RТТI), который предусмотрен во всех объектах |
ToString() | Возвращает символьное представление объекта в формате < имя_пространства_имен > . <имя-класса> (такой формат носит также название «полностью определенного имени» - fully qualified пате). Если тип определен вне какого-либо пространства имен, возвращается только имя класса. Этот метод может бьггь замещен для представления информации о внутреннем состоянии объекта (в Формате имя - значение) |
Finalize() | Пока мы будем считать, что основное назначение этого метода - освободить все ресурсы, занятые объектом данного класса, перед удалением этого объекта. |
MemberwiseClone() | Этот метод предназначен для создания еще одной ссылки на область, занимаемую объектом данного типа в оперативной памяти. Этот метод не может быть замещен. Если вам потребовалось реализовать поддержку создания полной копии объекта в оперативной памяти, вы должны реализовать в вашем классе поддержку интерфейса ICloneable. |
Комментариев нет:
Отправить комментарий