Введение в С# для программистов (A
programmer's Introduction to C#)
Автор
- Eric Gunnerson
перевод - Трубецкой А. На главную Содержание Глава 4
Примечание переводчика:
Перевод начинается с 3 главы, поскольку в 1 главе речь шла о том, что
такое объекты, инкапсуляция и полиморфизм. Мне показалось, что нет
большого смысла это переводить, поскольку все и так это знают. 2 глава
повествовала о среде разработки .NET. Эта информация могла бы быть
интересной, но мне хотелось в первую очередь сосредоточить внимание на
языке. Поэтому перевод 2 главы - это, возможно, дело будущего.
Глава 3: C# Быстрый старт
Краткий обзор
Эта глава представляет собой краткий обзор языка C#. Предполагается,
что читатель имеет определенный уровень знания программирования и
поэтому материал представлен не очень подробно. Если приведенное
объяснение останется непонятным, Вы можете найти более детальное
объяснение нужной темы далее в этой книге.
Привет, Вселенная
using System;
class Hello { public static void Main(string[] args) { Console.WriteLine("Hello, Universe");
// iterate over command-line arguments, // and print them out for (int arg = 0; arg < args.Length; arg++) Console.WriteLine("Arg {0}: {1}", arg, args[arg]); } }
.NET Runtime имеет единое
пространство имен (namespace) для всего кода программы (или метаданных).
Объявление using System дает
возможность ссылаться на классы, которые находятся в пространстве имен System, так что их можно
использовать, не добавляя System. перед именем типа.
Пространство имен System
содержит много полезных классов. Одним из них является класс Console, который используется при
создании консольных приложений.
Поскольку в C# нет глобальных функций, в данном примере объявляется
класс Hello, который содержит функцию static
Main(), служащую начальной точкой выполнения программы. Функция Main() может быть объявлена без
параметров или с параметром, который представляет собой массив строк.
Поскольку Main() является точкой
входа, она должна быть статической функцией. Это означает, что она не
связана с конкретным объектом класса, в котором она объявлена.
Первая строка функции Main()
вызывает функцию WriteLine()
класса Console, которая выводит
строку "Привет, Вселенная" на консоль. Цикл for перебирает параметры,
полученные функцией Main() и
затем выводит их на консоль с новой строки.
Пространство имен и
директива using
Пространства имен в .NET
Runtime используются для организации классов и других типов в единую
иерархическую структуру. Правильное использование пространств имен
делает классы более удобными в использовании и предотвращает конфликты с
классами, написанными другими авторами.
Пространства имен можно также рассматривать как способ определить
длинные имена для классов и других типов и при этом не писать каждый раз
полное имя.
Пространства имен определяются с помощью ключевого слова namespace. Если нужна многоуровневая
организация, пространства имен могут быть вложенными:
namespace Outer {
namespace Inner {
class MyClass {
public static void Function() {} } } }
Однако при этом приходится много печатать и выравнивать, поэтому эта
конструкция может быть упрощена, если ее записать следующим образом:
namespace Outer.Inner {
class MyClass {
public static void Function() {} } }
В каждом исходном файле может быть определено столько пространств
имен, сколько необходимо программисту.
Как упоминалось выше, директива using
позволяет пользователю опускать названия пространств имен при
использовании типа, в связи с чем использование типов становится более
простым. using - это просто ярлык,
который уменьшает количество печатаемого текста, т.е. using требуется при ссылке на
элементы, как показано в следующей таблице:
использование
using
строка
кода
не используется
System.Console.WriteLine("Hello");
using System
Console.WriteLine("Hello");
Обратите внимание, что using не
может использоваться с именем класса, чтобы затем имя класса могло быть
опущено. Другими словами, строка using
System.Console будет ошибочной.
Конфликты имен, возникающие между типами или пространствами имен,
которые имеют одинаковое название, всегда могут быть решены путем
написания полного имени типа. Имя может быть очень длинным, если класс
является глубоко вложенным, поэтому существует следующий вариант
использования using, который
позволяет определить псевдоним для класса:
using ThatConsoleClass = System.Console;
class Hello {
public static void Main() {
ThatConsoleClass.WriteLine("Hello"); } }
Чтобы код было легче воспринимать, в примерах этой книги практически не
используются пространства имен, но их необходимо использовать в реальном
коде.
Базовые типы данных
C# поддерживает обычный набор типов данных. Для каждого типа данных,
который поддерживается C#, существует соответствующий тип .NET Common
Language Runtime. Например, типу int
соответствует тип System.Int32. Практически всегда можно использовать
System.Int32 вместо int, но
этого не рекомендуется делать, потому что в этом случае код становится
более сложным для восприятия.
Основные типы данных описаны в приведенной ниже таблице. Типы времени
выполнения можно найти в
пространстве имен System из .NET Common Language Runtime.
Тип
Количество
байт
Тип
времени выполнения
Описание
byte
1
Byte
Unsigned byte
sbyte
1
SByte
Signed byte
short
2
Int16
Signed short
ushort
2
UInt16
Unsigned short
int
4
Int32
Signed integer
uint
4
UInt32
Unsigned int
long
8
Int64
Signed big integer
ulong
8
UInt64
Unsigned big integer
float
4
Single
Floating point number
double
8
Double
Double-precision floating point
number
decimal
8
Decimal
Fixed-precision number
string
String
Unicode string
char
Char
Unicode character
bool
Boolean
Boolean value
Различие между базовыми (или встроенными) типами в C# является в
значительной степени искусственным, поскольку определяемые пользователем
типы могут вести себя так же, как встроенные. Фактически, единственное
реальное отличие встроенных типов данных от типов, определяемых
пользователем, заключается в том, что существует возможность задавать
константные значения (literal values) для встроенных типов.
Типы данных делятся на обычные (или простые) и ссылочные типы. Обычные
типы хранятся в стеке или
встраиваются в структуру, полем которой они являются. Для ссылочных
типов выделяется место в куче (heap).
И ссылочные, и обычные типы являются производными от базового класса
object. В тех случаях, когда
обычный тип должен вести себя как объект, создается оболочка (wrapper),
которую можно рассматривать как ссылочный объект, помещенный в кучу, и в
нее копируется значение переменной обычного типа. Оболочка автоматически
помечается таким образом, что система знает, какое значение она
содержит. Этот процесс известен как упаковка
(boxing), а обратный процесс называется распаковкой
(unboxing). Упаковка происходит автоматически, для этого нужно только
присвоить значение обычного типа переменной типа object. Упаковка и
распаковка позволяют обрабатывать любой тип как объект. Таким образом,
можно написать следующее:
using System; class Hello {
public static void Main(string[] args) {
Console.WriteLine("Value is: {0}", 3.ToString()); } }
Здесь целое число 3 упаковывается, и для упакованного значения
вызывается функция Int32.ToString().
Массивы в C# могут быть многомерными (multidimensional) или
невыровненными (jagged). Более сложные структуры данных такие, как стек
и хеш-таблица, можно найти в пространстве имен System.Collections.
Классы, структуры и
интерфейсы
В C# ключевое слово class
используется для объявления ссылочного (размещаемого в куче) типа, а
ключевое слово struct - для
объявления обычного типа. Структуры используются для облегченных
(lightweight) объектов, которые должны вести себя как встроенные типы, в
остальных случаях используются классы. Например, int является обычным типом, а string - ссылочным типом. Приведенная
ниже схема показывает, как они работают:
int v = 123;
string s = "Hello There";
Рисунок 3-1. Обычные и ссылочные
типы
C# и .NET Runtime не поддерживают множественное наследование для
классов, но поддерживают возможность реализации множества интерфейсов.
Инструкции (statements)
Инструкции в C# похожи на инструкции C++, но имеют несколько отличий,
которые помогают избегать некоторых ошибок при их использовании. Кроме
того, введены несколько новых инструкций:
Инструкция foreach позволяет
получить доступ ко всем элементам массива или коллекции поочередно, в
порядке возрастания индексов.
Инструкция lock используется,
если программа имеет два или более потоков выполнения. При этом может
возникнуть ситуация, когда два потока пытаются использовать один ресурс.
Для того, чтобы избежать такой ситуации, можно создать блок критического
кода. В каждый момент времени доступ к этому блоку может иметь только
один поток. Этот блок создается с помощью ключевого слова lock.
Инструкции checked и unchecked позволяют управлять
проверкой переполнения при арифметических операциях и преобразованиях.
Если часть кода помечена как checked,
то в случае переполнения генерируется исключение. При использовании unchecked в случае переполнения
результат просто усекается.
Перечисления (enums)
Перечисления используются для объявления набора связанных по смыслу
констант (например, набора цветов, в которые может быть окрашен контрол)
ясным и безопасным способом. Например:
enum Colors { red, green, blue }
Делегаты и события
(delegates and events) Делегаты
представляют собой безопасную, объектно-ориентированную реализацию
указателя на функцию. Делегаты используются в ситуациях, когда
какой-либо компонент должен обратиться к компоненту, который его
использует. Наиболее эффективно делегаты используются в качестве
основания для событий,
при этом делегат легко регистрируется как обработчик события.
Свойства и индексаторы
C# поддерживает свойства
и индексаторы,
которые помогают отделить интерфейс объекта от его реализации. Вместо
того, чтобы разрешить пользователю обратиться к полю или массиву
напрямую, свойство или индексатор позволяют определить блок операторов,
реализующих доступ, если использование поля или массива разрешено.
Приведем простой пример:
using System; class Circle { public int X { get { return(x); } set { x = value; //draw the object here. } } int x; }
class Test { public static void Main() { Circle c = new Circle(); c.X = 35; } }
В этом примере get и set являются средствами доступа и
вызываются, когда происходит обращение к свойству Х.
Атрибуты Атрибуты
используются в C# и .NET Frameworks для передачи описательной информации
от автора кода другим частям кода, если они заинтересованы в этой
информации. Атрибуты можно использовать, чтобы определить, какое поле
объекта должно быть сериализовано, как отображать класс в браузере для
классов и др.
Атрибуты записываются в квадратных скобках. Например, атрибут может
выглядеть так:
[CodeReview("12/31/1999", Comment="Well done")].
Информация, содержащаяся в атрибуте, восстанавливается во время
выполнения с помощью процесса, называемого рефлексией (reflection).
Можно легко писать новые атрибуты, применять их к элементам кода (к
классам, полям, параметрам и т.д.) и восстанавливать их с помощью
рефлексии.