Введение в C#

 

В процессе создания C# преследовалось несколько целей:

 

·   Создатъ nервый объектно-ориентированный язык в семейства C/C++. Программирование все в меньшей степени сводится к построению монолитных nриложений и все больше ориентируется на построение компонентов, применяемых в различных рабочих средах, важнейшим для таких компонентов является наличие у них свойств, методов и событий, а также атрибутов, содержащих информацию о компоненте. Все эти концепции реализованы в С# на уровне основных языковых конструкций, благодаря чему зтот язык очень естественно подходит для создания и использования программных компонентов.

·   Создать язык, в котором любые данные nредставляют собой обьекты. Благодаря новаторскому применению таких концепции, как упаковка и распаковка, С# сокращает разрыв между примитивными типами и классами, позволяя интерпретировать любой фрагмент данных как объект. Более того, в С# представлена концепция структурных типов, при помощи которых пользователи могут создавать облегченные объекты, не требующие выделения памяти из кучи (heap).

·      Обеспечить возможность создания nрограмм, надежных и усточивых к ошибкам. C самого начала в С# бьrли заложены такие возможности, как сборка мусора, структурированная обработка исключений и безопасность типов. Эти концепции полностью исключают целые категории ошибок, часто встречающиеся в программах С++.

·      Упростить С++ с сохранением рабочих навыков nрpaммucти усилий, потраченных на освоение языка. С# имеет много общего с C++, и программист сразу же почувствует себя в знакомой обстановке. Кроме того, С# хорошо интегрирован с механизмами СОM и DLL, что обеспечивает возможность полноценного использования существующего программного кода.

 

 

 

 

Знакомство с языком C#

Использование идентификаторов

Идентификатор – это имя, обозначающее элемент вашей программы. При выборе идентификатор нужно придерживаться нескольких правил:

·      Идентификатор может состоять из букв, цифр, символов подчеркивания;

·      Идентификатор может начинаться только с буквы, либо с символа подчеркивания.

Важно:

C# - case-sensitive язык.

Использование переменных

Переменная – это элемент для хранения данных. Переменные должны иметь уникальное имя (внутри своего пространства имен).

Описание переменной:

typeName variableName;

typeName – имя типа, variableName – имя переменной.

При описании переменной возможна ее инициализация:

int count = 0, min = -10;

string name=”Joe”;

Основные типы данных

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

Тип данных

Описание

Размер (бит)

Значение

int

Целые числа

32

-231…231-1

long

Целые числа

64

-263…263-1

float

Числа с плавающей запятой

32

-3.4 * 1038…3.4 * 1038

double

Числа с плавающей запятой двойной точности

64

-1.7 * 10308…1.7 * 10308

decimal

Денежные значения

128

Числа с 28 значащими цифрами

-79228162514264337593543950335…

79228162514264337593543950335

string

Последовательность символов

16 (на символ)

 

char

Символ

16

0…216-1

bool

Логический тип

8

true, false

Важно:

В C# запрещено использование неинициализированных переменных. Вторая строка вызовет ошибку во время компиляции:

      int count, min = -10;

      min = count;

Арифметические операторы

C# поддерживает следующие арифметические операторы:

·      (+) – сложение или конкатенация строк;

·      (-) – вычитание;

·      (/) – деление;

·      (*) – умножение;

·      (%) – модуль (взятие остатка).

Операторы (/), (*), (%) имею более высокий приоритет в сравнении с операторами (+), (-). Если операторы имеют один и тот же приоритет, то они выполняются слева направо.

Важно:

Тип результата зависит от типа операндов: выражение 7 / 2 будет иметь значение 3, не 3.5. Результат выражения 7.0 / 2.0 или 7 / 2.0 будет равен 3.5, причем результат будет типа double, не float. В C# числа, имеющие в своей записи точку (.) всегда преобразуются к double.

Логические операторы

Результатом использования таких операторов будет true или false:

·      (==) – равенство выражений;

·      (!=) – отрицание равенства;

·      (!) – отрицание;

·      (<)меньше;

·      (<=) – меньше, либо равно;

·      (>) – больше;

·      (>=) – больше, либо равно.

·      (&&) – логическое И;

·      (||) – логическое ИЛИ.

Операторы типа

·      is – проверяет, возможно ли преобразование ссылки на объект к определенному типу или интерфейсу;

·      as – не только проверяет, но и преобразует к определенному типу или интерфейсу;

·      typeofвозвращает тип объекта.

Поразрядные операторы

Поразрядные операторы чаще используются с целыми типами данных, но могут использоваться и с логическим типом:

·      (&)поразрядное И;

·      (|) – поразрядное ИЛИ;

·      (^) – поразрядное исключающее ИЛИ;

·      (>>) – сдвиг вправо, младшие биты отбрасываются, старшие заполняются нулями;

·      (<<) – сдвиг влево, старшие биты отбрасываются, а младшие заполняются нулями.

Приоритет операторов

В таблице перечислены операторы в порядке убывания приоритета.

Категория

Операторы

Первичные

(x), x++, x--, typeof

Унарные

-, !, ++x, --x, (type)x

Умножение и деление

*, /, %

Сложение и вычитание

+, -

Сдвиг

>>, <<

Отношение

<, >, <=, >=, is

Равенство

==, !=

Поразрядный И

&

Поразрядное исключающее ИЛИ

^

Поразрядный ИЛИ

|

Логическое И

&&

Логическое ИЛИ

||

Присваивание

=, *=, /=, %=, +=, -=, <<=, >>=, &=, |= ^=

 

Описание методов

Методом называется именованная последовательность действий. Метод еще называют функцией или подпрограммой. Описание метода:

      resultType methodName (parameters)

      {

           

            return resultValue;

}

         resultType – тип результата метода, либо void,

         methodName – имя метода,

         parameters – список параметров передаваемых методу (может отсутствовать).

         return – если метод должен возвращать значение resultValue типа resultType.

Важно:

C# не поддерживает глобальных методов, все методы описываются внутри класса.

Вызов метода

Вызов метода:

      methodName (arguments), либо

      variableName.methodName (arguments).

         methodNameимя метода,

         arguments – список аргументов (переменных, передаваемых методу).

variableName – указание на экземпляр класса, метод которого вызывается. Если вызов метода происходит внутри класса, которому он принадлежит, указание экземпляра класса необязательно.

Важно:

Скобки в вызове метода должны присутствовать в любом случае, даже если метод не содержит аргументов.

Перегрузка методов

Перегрузка метода возможна, когда необходимо изменить порядок, количество и тип параметров. Перегрузка по типу возвращаемого значения запрещена.

class simple

{

      int add(int x, int y)

      {

            return (x + y);

}

 

int add(int x, int, y, int z)

{

return (x + y + z)

}

}

Локальная область видимости

Открывающая ({) и закрывающая (}) скобки метода определяют область видимости.

Любая переменная, описанная в теле метода, доступна только в этом методе и исчезает, когда метод завершает свое выполнение, поэтому такие переменные называются локальными:

class simple

{

            void a()

            {

                  int count = 0;

}

     

void b()

{

            count++;

}

}

такое использование count ошибку – переменная вне области видимости.

Классовая область видимости.

Любая переменная, описанная в теле класса (такую переменную называют полем) доступна из любого метода этого класса:

class simple

{

      int count = 0;

      void inc()

{

      count++;

}

void dec()

{

      count--;

}

}

Команды: условные

if (booleanExpression)

{

     

}

else

{

     

}

         booleanExpression – выражение, результат которого равен true, либо false. Ветка else может быть опущена.

         Команда switch:

         switch (expression)

      {

            case constant:

                 

                  break;

            case constant:

                 

                  break;

           

            default:

                 

                  break;

      }

         expression – выражение, которое вычисляется однажды и его результат равен одной из constant меток. Либо, если значение выражения не указано явно, то выполняется блок default. Блок default может и отсутствовать. Существует несколько правил написания switch:

·      switch может быть использован только на основных типах данных, таких как int или enum;

·      constant должны быть константами;

·      constant должны быть уникальными значениями;

·      один и тот же блок может относиться к нескольким меткам, если метки не разделены другими блоками кода.

switch (day)

{

   case mon:

         value = 0;

         break;

   case tue:

   case wed:

   case thu:

         value = 1;

         break;

}

Важно:

В switch допускается использование return.

Команды: организации циклов

        while (booleanExpression) 
               statement

 

        for (initialization; booleanExpression; updateControlVariable)
                statement

 

        do
               statement
        while (booleanExpression);

booleanExpression – логическое выражение, пока оно имеет значение true цикл выполняется.

initialization – выполнение начальной инициализации управляющей переменной цикла (инициализация может быть опущена или возможна инициализация не только управляющей переменной цикла).

updateControlVariable – изменение управляющей переменной цикла (изменение может быть опущено или возможно изменение не только управляющей переменной цикла).

int j = 10;
for (int i = 0; i <= j; i++, j--)
{
        
}

Управление ошибками

Что бы отследить и проконтролировать ошибку, требуется:

·      писать код в try блоке. Команды блока try выполняются одна за другой до тех пор, пока не произойдет ошибка или не будет достигнут конец блока. В случае возникновения ошибки управление передается в обработчик ошибок catch.

·      для эффективной обработки ошибок писать не один обработчик ошибок catch. Если во время выполнения кода в блоке try произошла ошибка, то автоматически произойдет выбор наиболее подходящего обработчика ошибок и управление немедленно передается ему.

try
{
        int leftHandSide = Int32.Parse(leftHandSideOperand.Text);
        int rightHandSide = Int32.Parse(rightHandSideOperand.Text);
        int answer = doCalculation(leftHandSiderightHandSide);
        result.Text = answer.ToString(); 
}
catch (FormatException fEx)
{
        
}
catch (OverflowException oEx)
{
        
}
 
Для того чтобы гарантированно выполнить какие-либо действия используйте блок finally:
 
TextReader reader = null;  
try 
{ 
    reader = src.OpenText(); 
    string line; 
    while ((line = reader.ReadLine()) != null) 
    { 
        source.Text += line + "\n"; 
    } 
} 
finally 
{ 
    if (reader != null) 
    { 
        reader.Close(); 
    } 
}
 

Использование проверки целочисленной арифметики

По умолчанию, компилятор C# позволяет типам переполнение. Можно управлять компилятор непосредственно, либо осуществлять проверку динамически.

Использование checked – блока позволяет контролировать переполнение, порождая исключение OverflowException.

int number = Int32.MaxValue;
checked
{
        int willThrow = number++; 
        Console.WriteLine("this won't be reached");
}

или

int willThrow = checked(Int32.MaxValue + 1);

Использование unchecked - блока позволяет отключить проверку на переполнение:

int number = Int32.MaxValue;
unchecked
{ 
    int wontThrow = number++; 
    Console.WriteLine("this will be reached");
}

ити

int willThrow = checked(Int32.MaxValue + 1);

Важно:

В checked блоке проверяются на переполнение только непосредственные арифметические операции, использование в блоке вызова метода не приведет к проверке на переполнение в данном методе.

Арифметика c плавающей точкой никогда не порождает OverflowException исключения, так как .NET Framework имеет представление бесконечности.

Порождение исключений

Иногда порождение исключений бывает полезно для отслеживания ошибок на некоторых уровнях приложения. Порождение исключения:

public static string monthName(int month) 
{ 
   switch (month) 
   { 
      case 1 :  
          return "January"; 
      case 2 : 
          return "February";  
      ... 
      case 12 : 
          return "December";  
      default : 
          throw new ArgumentOutOfRangeException("Bad month"); 
   }
}

Использование классов

В C# используется ключевое слово class для описания класса. Все методы, свойства класса описываются в теле класса:

class Circle 
{ 
    double Area()  
    { 
        return Math.PI * radius * radius;  
    } 
  
    double radius;
}

Создание и инициализация переменной (экземпляра класса):

Circle c;
c = new Circle();

Доступность членов класса

В классе circle нам недоступен ни метод, ни поле из внешней среды использования. Поля и метод может быть использованы только внутри класса (по умолчанию они private). Управление доступностью членов класса осуществляется посредством ключевых слов private и public:

Ÿ        private устанавливает доступность только внутри класса

Ÿ        public устанавливает доступность не только внутри класса, но и извне.

class Circle 
{ 
    public double Area()  
    { 
        return 3.141592 * radius * radius;  
    } 
  
    private double radius; 
}

Важно:

Поля класса автоматически инициализируются значениями 0, false, null в зависимости от типа поля.

Конструкторы

При использовании new CLR выделяет память и вызывает конструктор. Конструктор это специальные метод, который называется также как и класс, может иметь параметры, но не может возвращать значение (даже void). Каждый класс должен иметь конструктор, если вы его не создадите, то конструктор создастся по умолчанию.

class Circle 
{ 
    public Circle()
    { 
        radius = 0.0; 
    } 
 
    public double Area()  
    { 
        return 3.141592 * radius * radius;  
    } 
  
    private double radius;
}

Теперь можно использовать класс:

Circle c; 
c = new Circle(); 
double areaOfCircle = c.Area();

Перегрузка конструктора

Конструктор, как и все остальные методы можно перегружать:

class Circle 
{
 
    public Circle()
    { 
        radius = 0.0; 
    } 
 
    public Circle(double initialRadius)
    
        radius = initialRadius
    } 
 
    public double Area()  
    {  
        return 3.141593 * radius * radius;  
    } 
  
    private double radius; 
}

Важно:

Порядок описания конструкторов не имеет значения.

Частичные классы 

Ключевое слово partial. Идеальный способ для разделения кода и представления Удобство для разработчиков.

partial class Circle 
{ 
   public Circle()
    { 
        this.radius = 0.0; 
    } 
 
    public Circle(double initialRadius)
    {  
        this.radius = initialRadius
    } 

В другом файле:

partial class Circle 
{ 
     public double Area()  
    {  
        return Math.PI * radius * radius;  
    } 
  
    private double radius; 
}

Статические методы и данные

В C# все методы описываются внутри класса. Однако, если описать метод с ключевым словом static, то этот метод или поле будет доступно по имени класса (не экземпляра класса). Статическое поле:

public double Area()  
    {  
        return Math.PI * radius * radius;  
    }

Статический метод:

class Math 
{ 
    public static double Sqrt(double d) { ... } 
    ...
}
...
double d = Math.Sqrt(9);

Использование ключевого слова const делает поле статическим по умолчанию:

class Math 
{ 
    ... 
    public const double PI = 3.14159265358979; 
}

Статический класс

Класс определенный ключевым словом static может содержать только статические методы и поля. Основное назначение таких классов – утилитарное использование методов и полей. Невозможно создать экземпляр такого класса.

public static class Math 
{ 
    public static double Sin(double x) {…} 
    public static double Cos(double x) {…} 
    public static double Sqrt(double x) {…} 
    ... 
}

Общие типы (Generics)

Общие  типы (generics) позволяют при описании классов, структур, методов и интерфейсов использовать параметризованные параметры (не указывать тип параметра в момент написания кода). Тип параметра определяется в момент объявления переменной соответствующего типа. Таким образом, можно создать некоторый общий элемент, тип который можно использовать в дальнейшем для данных различных типов.

Например, создадим класс используя общие типы:

class Generics<TYPE1, TYPE2>
{
  private TYPE1 mVar1;
  private TYPE2 mVar2;
 
  public Generics(TYPE1 Var1, TYPE2 Var2)
  {
    this.mVar1 = Var1;
    this.mVar2 = Var2;
  }
  public string ToStringFunction(string Delemiter)
  {
    return this.mVar1.ToString() + Delemiter + this.mVar2.ToString();
  }
 
  public TYPE1 Variable1
  {
    get
    {
      return this.mVar1;
    }
    set
    {
      this.mVar1 = value;
    }
  }
 
  public TYPE2 Variable2
  {
    get
    {
      return this.mVar2;
    }
    set
    {
      this.mVar2 = value;
    }
  }
}

Как видно из примера, для того чтобы использовать общие типы нужно после объявления класса указать параметризованные типы: <TYPE1, TYPE2> объявляет класс с двумя параметризованными типами. Теперь используем написанный класс:

Generics<string, string> strGeneric = new Generics<string, string>("Hello","world");
Generics<int, int> intGeneric = new Generics<int,int>(1, 2);
Generics<string, int> strintGeneric = new Generics<string,int>("Three", 3);
int intSum;
string strSum;
 
intSum = intGeneric.Variable1 + intGeneric.Variable2;
strSum = strintGeneric.Variable1 + " " + strintGeneric.Variable2.ToString();
 
MessageBox.Show("\nstrGeneric:\n" + strGeneric.Variable1 + " " + strGeneric.Variable2 + 
"\n\nintGeneric sum:\n" + intSum.ToString() +
"\n\nstrintGeneric sum:\n" + strSum.ToString());

Таким образом, очевидно, что создан класс который можно использовать с параметрами различных типов, которые устанавливаются в момент объявления класса.

Преимущества использования общих типов

Какие же преимущества дает использование общих типов?

1.     Наиболее очевидное - повторное использование кода. Нет необходимости создавать два идентичных класса, отличающихся только типами параметров, достаточно создать один с параметризованными типами. При этом использование параметризованных типов позволяет создавать единый программный код для работы с различными типами данных. Например, единожды написанный алгоритм может работать и с целыми числами и с числами с плавающей десятичной точкой, при этом не производя на каждом шаге проверку/приведение типа. Так Generics вытесняют классы объявленные с использованием типа object.

2.     Повышение производительности кода по сравнению с использование параметров типа object - нет необходимости выполнять приведение, как уже сказано выше, на каждом шаге, за счет чего получается выигрыш в производительности.

3.     Проверка типов в момент компиляции программы. Поскольку не используются параметры типа object, то компилятор может выполнить проверку типа каждого параметра в момент компиляции, поскольку типы для Generic классов жестко задаются в момент объявления переменных классов этого типа.

Общие типы не соответствуют шаблонам в C++, поскольку параметризованные типы в C# не могут быть использованы в качестве базовых классов для общих типов. Также в C# не допускается использования Generic классов в качестве параметров типов в других Generic классах.

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

Коллекции

·      ArrayList

·      Queue

·      Stack

·      Hashtable

·      SortedList

 

ArrayList list=new ArrayList();

list.Add(3);

list.Add(“string”);

int val=(int)list[0];

Вариант решения – обобщенные коллекции:

ArrayList<int> list=new ArrayList<int>();

list.Add(3);

...

int val=list[0];

Безымянные или анонимные методы

Безымянные методы (anonymous methods) позволяют значительно сократить объем кода, который должен написать разработчик. Наиболее простое и понятное использование безымянных методов при назначении относительно простых обработчиков событий. Рассмотрим пример, пусть у нас есть форма, на которой размещены текстовое поле txtLogin и кнопка btnLoginи нам нужно, чтобы при изменении текста в текстовом поле, изменялся текст кнопки. Разумеется, что для этого необходимо в обработчике события TextChanged изменять текст кнопки.

Какой код создает Visual Studio при добавлении нового обработчика события? Прежде всего функцию обработчик и запись о соответствии функции обработчика событию, что осуществляется присвоением соответствующего делегата соответствующему событию контрола TextBox функции InitializeComponent:

this.txtLogin.TextChanged += new System.EventHandler(this.txtLogin_TextChanged);

Сам обработчик выглядит так:

private void txtLogin_TextChanged(object sender, EventArgs e)
{
  btnLogin.Text = "Login [" + txtLogin.Text + "]";
} 

Те же операции можно выполнить вручную создав, например, такой код:

public frmMain()
{
  InitializeComponent();
  this.txtLogin.TextChanged += new System.EventHandler(this.txtLogin_TextChanged);
}
private void txtLogin_TextChanged(object sender, EventArgs e)
{
  btnLogin.Text = "Login [" + txtLogin.Text + "]";
} 

Теперь же перепишем этот код с использованием безымянных методов:

public frmMain()
{
  InitializeComponent();
  this.txtLogin.TextChanged += delegate { btnLogin.Text = "Login [" + txtLogin.Text + "]"; };
}

Таким образом, мы поместили код обработчика непосредственно в место присваивания делегата событию TextChanged. Формально этот процесс ничего не меняет - в момент  компиляции компилятор сам создаст функцию и присвоит ей некоторое имя, а потом создаст делегата для этой функции и присвоит его событию TextChanged. Но отметьте, на сколько меньше строк кода нужно для реализации, при этом никаких потерь производительности после компиляции не будет, поскольку в результате будет получен такой же код, что и в примерах описанных выше.

Поведение свойств

В C# существует возможность ограничивать видимость get и set блоков свойств классов, например:

public partial class User
{
  public int MyInt{
    get{return this.mInt;}
    private set{this.mInt = value;}
  }
}

Таким образом, только внутри класса можно будет установить свойство MyInt, "извне" такая возможность будет недоступна.

Итераторы 

В основном для foreach и интерфейсов IEnumerator, IEnumerable. Реализация метода, возвращающего IEnumerator:

public class List

{

   public IEnumerator GetEnumerator() {

      for (int i = 0; i < count; i++)

     
         yield return elements[i];

      }

   }

}

Сайт создан в системе uCoz