oldi

Изучаем C#. Часть 2. Концепции языка

Алексей Федоров

Базовые синтаксические правила

Примитивные типы

Выражения и операторы

Методы

Ссылочные типы и типы со значениями

Условия

Циклы

Массивы данных

Обработка ошибок

 

На предыдущем занятии мы получили общее представление о языке C#, рассмотрели типы сборок, которые можно создавать с помощью этого языка, и обсудили концепцию сборок. В этот раз мы начнем знакомство с основными концепциями языка.

Мы уже видели несколько программ на языке C#, а также узнали о том, как создавать повторно используемые классы. Мы также выяснили, как создавать сборки с помощью пакетного компилятора. Одним словом, мы вполне готовы к тому, чтобы ознакомиться с синтаксическими конструкциями этого языка. Язык C# имеет схожий синтаксис с такими языками, как C или Java. Если вы знакомы с этими языками, то без труда распознаете фигурные скобки ({}), ключевое слово class, разделители (;) в конце каждой строки и т.п. Более того, язык C# покажется вам достаточно простым.

Базовые синтаксические правила

Давайте кратко перечислим базовые синтаксические правила языка C#. Их не так много:

  • C# является языком, в котором учитывается регистр символов. Это означает, что имена методов и переменных различаются по их регистру. Например, метод с именем Print() и метод с именем print() — это два разных метода в языке C#;
  • пробелы (собственно пробелы, символы табуляции и символы «возврат каретки») не имеют никакого значения. Например, оба следующих фрагмента кода идентичны для компилятора C#:
    if (x==y) {     Console.WriteLine("Equal");}          
               
               
    if (x==y){          
              Console.WriteLine("Equal");          
    }          

    Пробелы используются в основном для улучшения внешнего вида исходного кода программы;

  • символ «точка с запятой» (;) используется для указания на конец выражения;
  • выражения могут быть объединены в блок, который располагается внутри фигурных скобок ({}). Блоки кода могут быть вложенными.
В начало В начало

Примитивные типы

В языке C# есть ряд примитивных типов, которые реализованы на уровне библиотеки классов .NET Framework Class Library. Эти типы отличаются от остальных тем, что C# позволяет выполнять с ними такие операции, как непосредственное присваивание, математические операции и сравнение. Например, следующая строка кода вполне допустима в C#, так как обе переменные имеют тип Int32:

x = x + y;

Тем не менее та же самая строка кода будет содержать ошибку, если тип переменных x и y будет не примитивным, например Form или FileStream.

Также интересно отметить, что для примитивных типов в C# существуют псевдонимы. Например, можно объявить тип переменной как Int32 или int — и в том, и в другом случае тип переменной будет Int32. Выбор имени типа (оригинальное или псевдоним) — дело вкуса. Важно помнить, что и примитивный тип, и его псевдоним на самом деле являются одним и тем же типом. Примитивные типы, доступные в языке C#, показаны в табл. 1.

Говоря о примитивных типах, нужно отметить следующее:

  • в языке C# существует тип String, использование которого не требует представления строк в виде массива типа Char, как это делается в языках типа C или C++;
  • в языке C# существует тип Boolean, и там, где в выражениях требуется именно этот тип, нельзя использовать численные типы. В языке C# также есть два ключевых слова для обозначения true и false;
  • используемые в C# типы, определенные в библиотеке классов как примитивные, являются объектами (наследуют от Object) и имеют методы и свойства — это наглядно показано на следующем рисунке.

В этом примере показано использование некоторых примитивных типов языка C#.

static void Main(string[] args)

      {  
            Int32 x = 100;  
            Int32 y = x + 100;  
            Console.WriteLine(y);  
   
            Decimal d = 20;  
            Decimal e = d/7;  
            Console.WriteLine(e);  
   
            String s = "Learning ";  
            String t = s + "C#";  
            Console.WriteLine(t);  
                    
            Boolean f = (x==y);  
            Console.WriteLine("\t" + f);  
            Console.WriteLine();  
      }  
В начало В начало

Выражения и операторы

Выражения являются неотъемлемой частью любого языка программирования, и в языке C# мы найдем обширный набор выражений и операторов. Операторы типа (+), (*) и (/) служат для манипуляции над данными, хранимыми в переменных, и выполняются в определенном порядке, который, как и в других языках программирования, может быть изменен с помощью скобок, например:

int x = 100 * (a - b);

Язык C# — это строго типизированный язык, и выражения в нем также строго типизированы. Например, приведенная выше строка кода является выражением с типом Int32 — компилятор автоматически выполняет преобразования между примитивными типами. Тем не менее ряд преобразований, вполне допустимых в языках C или C++, невозможен в языке C#. Так, недопустимым является преобразование из типа Int32 или любого другого числового типа в тип Boolean. Приведенный ниже код вполне допустим в языке C, но мы получим сообщение об ошибке при попытке компиляции этого кода в C#:

public static void WhileTest(){  
   
      Int32 x = 10;  
      while(x--) {}  
}  

В табл. 2 показаны операторы, которые можно использовать в выражениях на языке C#.

В начало В начало

Методы

В языке C#, как в большинстве других языков программирования, код группируется в функции. Но в отличие от некоторых языков программирования, в C# функции всегда являются членами какого-либо типа, обычно таким типом выступает класс. В этом случае функции называются методами, которые могут иметь любое число параметров или не иметь их вообще. Методы могут возвращать любой тип или не возвращать ничего — в этом случае возвращаемый тип указывается как void.

Можно выделить два основных типа методов — статические методы и методы экземпляров. Статические методы объявляются с использованием ключевого слова static и могут вызываться без создания экземпляра типа, в котором они реализованы. Синтаксис для вызова статических методов выглядит так:

TypeName.MethodName();

В приведенном ниже примере показано задание и использование нескольких статических методов:

static void Main(string[] args)  
    {  
     DemoClass.ClassMethod();  
   
     DemoClass obj = new DemoClass();  
     Int32 z = obj.Math(12, 13);  
     Console.WriteLine(z);  
    }  
            
class DemoClass{  
    public static void ClassMethod()  
    {  
     Console.WriteLine("DemoClass.ClassMethod");  
    }  
    public Int32 Math(Int32 x, Int32 y)  
    {  
     return x+y;  
    }  

Итак, мы создали класс DemoClass, который содержит два метода — ClassMethod() и Math(). Метод ClassMethod() является статическим, метод Math() — методом экземпляра. При вызове метода ClassMethod нам не требуется создания экземпляра класса DemoClass, но для вызова метода Math() мы сначала создаем экземпляр класса DemoClass. Также обратите внимание на то, что метод Math() имеет два параметра и возвращает значение, тогда как метод ClassMethod() параметров не имеет и значений не возвращает.

Завершая рассмотрение методов, скажем, что в приложениях на языке C# код структурируется созданием коллекции типов (чаще всего это классы) и методов, определенных в этих типах.

В начало В начало

Ссылочные типы и типы со значениями

В управляемом коде — коде, выполняющемся под управлением Common Language Runtime, может быть две категории типов: ссылочные типы и типы со значениями. Различие между ними заключается в том, где в памяти хранятся представляемые этими типами данные. Приведем следующий пример. Предположим, у нас есть два типа — String и Int32. String — это ссылочный тип, а Int32 — тип со значением. Оба этих типа — примитивные, наследуют от Object и имеют свойства и методы. Различаются же они принципом обработки переменных этих типов. Дело в том, что переменная типа String всегда является ссылкой на объект String, располагаемый в памяти, либо нулевой ссылкой. В то же время переменная типа Int32 — это просто целочисленное значение, и она не ссылается ни на какую область памяти. Таким образом, переменной типа String можно присвоить значение, равное нулевой ссылке (null), но этого нельзя сделать с переменной типа Int32. Вот как объявляются такие переменные:

Int32 x;
String s;

В первой строке объявлена переменная х типа Int32 — это означает, что переменная указанного типа создана с начальным значением, равным нулю. При объявлении переменной типа String не происходит создания экземпляра класса String — переменная s содержит нулевую ссылку. Она будет содержать это значение до тех пор, пока мы не присвоим ей значение экземпляра типа String.

Большинство типов в .NET Framework Class Library — это ссылочные типы, в то время как большинство примитивных типов (за исключением String и Object) — это типы со значениями. Используя документацию к .NET Framework Class Library, всегда можно определить категорию того или иного типа: если он указан как класс — это ссылочный тип, если же он указан как структура — это тип со значением. То же самое относится и к объявлению собственных типов: если мы описываем тип как class — это будет ссылочный тип, если же как struct — это будет тип со значением. В большинстве случаев следует использовать ссылочные типы.

В начало В начало

Условия

Как и в других языках программирования, в C# существуют конструкции для управления логикой работы программы. Наиболее распространенным является оператор if, который выглядит так:

if(/*условие*/){/*выполяемый код*/}

В паре с оператором if можно использовать оператор else, как показано в следующем примере:

public static void If_Else()  
      {  
            Int32 x = 10;  
            Int32 y = 20;  
   
            // Оператор if  
            if(y == x)  
            {  
                  Console.WriteLine("y равно x");  
            }  
   
            // Операторы if и else  
            if(y == x)  
            {  
                  Console.WriteLine("y равно x");  
            }  
            else  
            {  
                  Console.WriteLine("y не равно x");  
            }  
      }  

Помимо операторов if-else в языке C# существует оператор switch — он позволяет выбрать код по одному из условий или выполнить код по умолчанию. Это показано в следующем примере.

public static void switch_demo()  
      {  
            switch(x)  
            {  
                  case 1:  
                        Console.WriteLine("x равно 1");  
                        break;  
                  case 2:  
                        Console.WriteLine("x равно 2");  
                        break;  
                  default:  
                        Console.WriteLine("x не равно ни 1, ни 2");  
                        break;  
            }  
      }  

Оператор break завершает выполнение оператора switch и передает управление коду, который следует за этим оператором.

В начало В начало

Циклы

В языке C# поддерживаются основные операторы циклов типа for и while, а также новый оператор foreach. Использование этих операторов показано на примере:

public static void LoopsDemo()  
      {  
            Int32 index;  
     
            // Цикл while  
            index = 10;  
            while(index != 0)  
            {  
                  Console.WriteLine(index);  
                  index--;  
            }  
   
            // Цикл for  
            for(index = 0;index<100;index++)  
            {  
                  Console.Write(index);  
                  Console.Write("\t");  
            }  
   
            // Цикл do-while  
            index = 0;  
            do  
            {  
                  Console.WriteLine("Как минимум 1 раз");           
            }while(index < 0);  
   
            // Цикл foreach  
            Int32[] myArray = new Int32[]{10, 20, 30, 40};  
            foreach(Int32 i in myArray)  
            {  
                  Console.WriteLine(i);  
            }  
      } 

Циклы while, for и do-while работают точно так же, как и в языках C и C++. Ключевое слово foreach — это новинка, появившаяся в языке C#. Цикл foreach используется для перебора элементов в коллекции. Коллекцией может быть либо экземпляр соответствующего класса, либо массив (как на приведенном выше примере). Синтаксис для foreach несколько отличается от синтаксиса цикла for: здесь не требуется управления итерацией и использования индексной переменной. Все, что необходимо указать, — это тип элементов в коллекции (например, Int32 на приведенном выше примере), имя переменной для текущего элемента (i на приведенном выше примере) и имя коллекции (myArray на приведенном выше примере).

В начало В начало

Массивы данных

В языке C#, как и во многих других языках программирования, поддерживаются массивы. Основные концепции массивов схожи с другими языками, но имеются и некоторые различия. Рассмотрим пример:

public static void ArraysDemo()  
      {  
        
            // Одномерный массив из 100 байт  
            Byte[] bytes = new Byte[10];  
   
            // Двухмерный массив из 4 Int32  
            Int32[,] ints = new Int32[5,5];  
   
            // Одномерный массив ссылок на строки (String)  
            String[] strings = new String[10];  
   
    }  

На приведенном примере показано объявление трех массивов. Первые два массива — это одномерный и двухмерный массивы, содержащие элементы типа Byte и Int32 cоответственно. Синтаксис для объявления таких массивов достаточно прозрачен и должен быть знаком из языков C и C++. Третий пример — это одномерный массив ссылок на строки. В этом случае не создаются объекты типа String — каждый элемент такого массива представляет собой нулевую ссылку.

Отметим, что массивы в .NET — это объекты, наследуемые от класса Array, предоставляемого библиотекой классов. Это достаточно удобно, так как класс Array содержит множество полезных методов. Массивы являются ссылочными типами, но их элементы могут быть как ссылочными типами, так и типами со значениями.

В начало В начало

Обработка ошибок

Обработка ошибок в коде на языке C# всегда выполняется через структурную обработку исключений (structured exception handling). Это означает, например, что мы не должны создавать методы, которые возвращают код ошибки. Вместо этого методы должны генерировать исключения, а вызывающий код сам определит наличие ошибки. Практически все классы в библиотеке классов Microsoft .NET Framework Class Library генерируют исключения, указывая на возникновение каких-либо проблем. Таким образом, в нашем коде мы должны обработать одно или более исключений, даже если наш собственный код не генерирует их. В противном случае выполнение приложения может завершиться раньше времени — в результате необработанного исключения.

На следующем примере показана обработка исключений:

public static void Exception()  
      {  
      // Блок try-catch  
            try  
            {  
                  Int32 index = 10;  
                  while(index-- != 0)  
                  {  
                        Console.WriteLine(100/index);  
                  }  
            }  
            catch(DivideByZeroException)  
            {  
            Console.WriteLine("Исключение - деление  на 0 ");  
            }  
            Console.WriteLine("Исключение обработано, продолжаем ");  
   
      // Блок try-finally  
            try  
            {  
                  return;  
            }  
            finally  
            {  
            Console.WriteLine("Выполняем блок finally ");  
            }  
      }  

Цикл внутри блока try-catch на приведенном примере в какой-то момент достигает значения 0 и генерируется исключение DivideByZeroException. Все исключения — это классы, базирующиеся на классе Exception, предоставляемом библиотекой классов. В блоке try-finally располагается код, который гарантированно будет выполнен, независимо от того, была исключительная ситуация или нет.

Итак, мы начали знакомиться с основными концепциями языка. Мы рассмотрели базовые синтаксические правила языка C#, примитивные типы, выражения и операторы, методы, ссылочные типы и типы со значениями, а также условия, циклы, массивы данных и обработку ошибок. В следующем номере мы продолжим разговор об основных концепциях языка — рассмотрим основы объектно-ориентированного программирования, использование типов, приведение типов, а также расширенные типы.

КомпьютерПресс 5'2002