Расширенное руководство C# (часть 1)

Введение

     
Это первая часть серии руководств по C #. В этой части мы представляем основные понятия языка и его выход - Microsoft Intermediate Language (MSIL). Мы рассмотрим объектно-ориентированное программирование (ООП) и то, что C # делает, чтобы сделать ООП настолько эффективным, насколько это возможно на практике.
Для дальнейшего чтения список ссылок будет приведен в конце. Ссылки дают более глубокий взгляд на некоторые из тем, обсуждаемых в этом уроке.
Немного предостережения перед чтением этого урока: Как работает этот учебник, он определяет путь к юзабилити. Эта часть учебника по существу необходима, прежде чем делать что-либо с C#. После этого первого учебника каждый должен иметь возможность написать простую программу на C#, используя основные объектно-ориентированные принципы. Следовательно, более продвинутые или любопытные читатели, вероятно, пропустят некоторые части. В качестве примера мы не будем изучать обработку исключений, захватывающие возможности с .NET-Framework, например LINQ, и более сложные языковые функции, такие как дженерики или лямбда-выражения. Здесь наша цель - дать мягкое введение в C# для людей, поступающих с других языков.

Правильная среда разработки

Прежде чем мы начнем что-то делать, мы должны подумать о том, где мы хотим что-то сделать. Логичным выбором для написания программы является текстовый редактор. В случае C# есть еще лучший вариант: с помощью Microsoft Visual Studio (VS)! VS Community Edition доступен для проектов с открытым исходным кодом без необходимости приобретения лицензии. Однако есть больше вариантов, которые были разработаны специально с учетом C#. Если мы хотим иметь межплатформенную IDE, то MonoDevelop - хороший выбор. В Windows бесплатная альтернатива VS и MonoDevelop - SharpDevelop. Доступны более общие параметры, такие как код Visual Studio (код VS), Sublime Text или Atom GitHub (для обозначения нескольких). Все они могут быть превращены в мощные C# IDE с помощью OmniSharp .

Краткий обзор Visual Studio 2012 IDE

Использование мощной среды разработки, такой как Visual Studio, поможет нам при написании программ с .NET-Framework. Такие функции, как IntelliSense (интеллектуальная версия автозаполнения, которая показывает нам возможные вызовы методов, переменные и доступные ключевые слова для определенной позиции кода), контрольные точки (выполнение программы может быть приостановлено и проверено на желаемых позициях кода) и графические редакторы для пазработки UI сильно помогут нам для эффективного программирования.

Кроме того, Visual Studio предоставляет интегрированный контроль версий, браузер объектов и инструменты документации. Еще один бонус для написания проектов C# с Visual Studio - это концепция решений и проектов. В Visual Studio обычно создается решение для проекта разработки. Файл решения может содержать несколько проектов, которые могут быть скомпилированы в библиотеки ( DLL) и исполняемые файлы ( файлы .exe). Обозреватель решений позволяет нам эффективно управлять даже крупномасштабными проектами.

Файлы проекта используются приложением MSBuild для компиляции всех необходимых файлов и ссылки на зависимости, такие как библиотеки или .NET-Framework. Нам, как разработчику, больше не нужно беспокоиться о написании make-файлов. Вместо этого мы просто добавляем файлы и ссылки (зависимости) к проектам, которые будут автоматически скомпилированы и связаны в правильном порядке.
Есть несколько ярлыков / функций, которые сделают использование VS настоящим удовольствием:
  • CTRL (and) SPACE , открывает IntelliSensе.
  • CTRL (and). , открывает меню.
  • F5 , для создания и начала отладки.
  • CTRL (and) F5 для построения и выполнения без отладки.
  • F6 , построение проекта.
  • F10 , чтобы перейти к следующей строке в текущей функции (шаг за шагом) при отладке.
  • F11 , чтобы перейти к строке в следующей или текущей функции (шаг в) при отладке.
  • F12 , чтобы перейти к определению идентификатора в позиции каретки.
  • SHIFT (and) F12 , чтобы найти все ссылки идентификатора в позиции каретки.
  • CTRL (and) + ,поиск по всему проекту.
Конечно, эти сочетания клавиш могут быть изменены и не требуются. Все, что можно сделать с помощью ярлыков, также доступно с помощью мыши. С другой стороны, есть больше возможностей и возможностей, чем ярлыки.
Где получить Visual Studio? Есть несколько вариантов, некоторые из них даже бесплатны. Студенты, обучающиеся в университете, который участвует в программе DreamSparks / MSDNAA, обычно имеют возможность бесплатно загрузить Visual Studio (до Ultimate). В противном случае можно загрузить общедоступные бета-версии или свободно доступную версию Community Edition. Раньше также существовали специализированные версии Visual Studio, связанные с языками, называемые Express Edition.
Свободно доступная версия Community Edition доступна на VisualStudio.com . Для отдельных разработчиков Visual Studio Community не устанавливает ограничений на то, какие приложения - возможно, это бесплатные или платные приложения - для разработки. Для организаций лицензия ограничивается использованием продукта в следующих сценариях:
  • в среде обучения в классе,
  • для академических исследований, или
  • за вклад в проекты с открытым исходным кодом.
В этом уроке мы сосредоточимся только на консольных приложениях. GUI будет представлен в следующем уроке. Чтобы создать новый проект консольного приложения в VS, мы просто должны использовать меню File: Затем мы выбираем New, Project. В диалоговом окне мы выбираем C# с левой стороны, а затем консольное приложение с правой стороны. Наконец, мы можем дать нашему проекту такое имя, как SampleConsoleApp . Это оно! Мы уже создали наше первое приложение C#.

Базовые концепты

C# - управляемый статический язык с сильным типизированием, с С-подобным синтаксисом  и объектно-ориентированными функциями, подобными Java. В целом можно сказать, что C# очень близок к Java для начала. В текущей версии C# есть несколько отличных функций, но в этом первом уроке мы их исключаем.
Управляемая память означает две вещи: Прежде всего, нам больше не нужно заботиться о памяти. Это означает, что люди, раньше изучавште  C или C ++, могут перестать беспокоиться о освобождении памяти, выделенной для своих объектов. Мы только создаем объекты и делаем что-то с ними. Как только мы перестанем их использовать, умная программа под названием «Сборщик мусора» (GC) позаботится о них. На следующем рисунке показано, как работает сборщик мусора. Он обнаруживает объекты без ссылок, собирает их и освобождает соответствующую память. Мы не имеем большого контроля над моментом времени, когда это происходит. Кроме того, GC будет выполнять некоторую оптимизацию памяти, однако это обычно делается не сразу после освобождения памяти.
Простое приближение того, как работает GC
Это приводит к некоторым накладным расходам на стороне памяти и производительности, но есть и преемущества, например невозможны ошибки сегментации на C#. Однако утечка памяти по-прежнему является проблемой, если мы сохраняем ссылки на объекты, которые больше не требуются.
Статический синтаксический языка означает, что компилятор C# должен знать точный тип каждой переменной и что система типов должна быть последовательной. Нет никакой привязки к типу данных void, который позволяет нам в основном что-либо делать, однако у нас есть тип данных, называемый Object, что может привести к аналогичным проблемам. Ниже мы обсудим последствия базового типа Object. Сильная часть дает нам подсказку о том, что операции могут использоваться только в том случае, если операция определена для элементов. Никаких явных преобразований не происходит без нашего ведома.

У нас есть еще одно следствие управления C#: C# не является native языком и не интерпретируется - это нечто среднее между ними. Компилятор не генерирует код сборки, это делает так называемый Microsoft Intermediate Language (MSIL). Этот трюк избавляет нас от перекомпилирования для разных платформ. В случае C# мы просто компилируем один раз и получаем так называемую сборку Common Language Runtime (CLR). Эта сборка будет выполняться Just-In-Time (JIT), скомпилированной во время выполнения. Другая особенность заключается в том, что оптимизация будет также выполняться во время выполнения. Часто встречающиеся вызовы методов будут вставлены в очередь, а не требуемые операторы будут автоматически опущены.
Теоретически это может привести к (независимо от используемых ссылок) платформа-независимым программам, однако это означает, что на всех платформах есть требования для запуска и компиляции CLR-сборок JIT. Сейчас Microsoft ограничивает .NET-Framework для семейства Windows, однако Xamarin предлагает продукт под названием Mono, который дает нам решение, которое также работает на Linux и Mac.
Возвращаясь к самому языку, мы увидим, что объектно-ориентированные функции вдохновлены функциями Java. В C# использовался только несколько другой набор ключевых слов. Ключевое слово extend Java заменено оператором c++ ":". Существуют и другие области, где двоеточие используется оператором с другим (но связанным) значением.
Давайте посмотрим на образец кода Hello Tech.Pro.

//Those are the namespaces that are (by default) included
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

//By default Visual Studio creates already a namespace
namespace HelloWorld
{
    //Remember: Everything (variables, functions) has to be encapsulated
    class Program
    {
        //The entry point is called Main (capital M) and has to be static
        static void Main(string[] args)
        {
            //Writing something to the console is easily possible using the
            //class Console with the static method Write or WriteLine
            Console.WriteLine("Hello Tech.Pro!");
        }
    }
}

Это похоже на Java, за исключением тела. Комментарии могут быть помещены с помощью косой черты (комментарий идет до конца строки без возможности переключения назад) или с использованием косой черты и символа звездочки. В последнем случае комментарий должен заканчиваться обратным, то есть звездочкой и косой чертой. Такой комментарий называется комментарием блока. Visual Studio также имеет специальный режим комментариев, который запускается после ввода трех косых черт.
Назад к коду: хотя нам не нужно размещать наши классы в пространстве имен, Visual Studio создает по умолчанию. Однако требуется создание класса. Каждый метод и переменная должны быть инкапсулированы. Вот почему C# считается сильно объектно-ориентированным. Инкапсуляция данных, как мы увидим, является важной особенностью объектно-ориентированного программирования и поэтому необходима в C#.
Есть уже некоторые другие вещи, которые мы можем извлечь из этого небольшого образца. Во-первых, С# использует (как и должен) класс Console для взаимодействия с консолью. Этот класс содержит только так называемые статические члены, такие как функция WriteLine. Статический член - это такой элемент, к которому можно получить доступ, не создавая экземпляр класса, т. е. статические члены не могут быть воспроизведены. Они существуют только один раз и существуют без явного создания. Функции класса называются методами.

Основная точка входа Main может существовать только один раз, поэтому она должна быть статичной. Другая причина заключается в том, что он должен жить в классе. Если бы он не был статическим, сначала должен был быть создан класс (поскольку все нестатические члены могут быть доступны только созданным объектам, называемым экземплярами класса). Теперь у нас проблема с курицей и яйцом. Как мы можем сказать программе о создании экземпляра класса (к которому можно обратиться к основному методу), не имея метода, в котором мы начинаем что-то делать? Следовательно, Main должен быть static.
Другое, что мы можем узнать, это то, что параметр args - это, очевидно, массив. Кажется, что массив используется как тип данных, тогда как в C переменная является массивом, а тип будет строкой. Это по дизайну и имеет некоторые полезные последствия. Позже мы увидим, что каждый массив основан на объектно-ориентированном принципе, называемом наследованием, который определяет базовый набор полей и реализаций. Каждый тип массива имеет поле Length, которое содержит количество элементов в массиве. Это уже не скрыто и должно быть передано в качестве дополнительного параметра, поэтому стандартный основной метод программы C# имеет только один параметр по сравнению со стандартным основным методом C с двумя параметрами.


Пространства имен



Все определяемые классы не существуют сами по себе, а, как правило, заключаются в специальные контейнеры - пространства имен. Создаваемый по умолчанию класс Program уже находится в пространстве имен, которое обычно совпадает с названием проекта:

namespace HelloApp
    class Program 
    {
        static void Main(string[] args)
        {
        }
    }
}

Пространство имен определяется с помощью ключевого слова namespace, после которого идет название. Так в данном случае полное название класса Program будет HelloApp.Program.
Класс Program видит все классы, которые объявлены в том же пространстве имен:

namespace HelloApp
    class Program 
    {
        static void Main(string[] args)
        {
             Account account = new Account(4);
        }
    }
    class Account
    {
        public int Id { get; private set;} // номер счета
        public Account(int _id)
        {
            Id = _id;
        }
    }
}

Но чтобы задействовать классы из других пространств имен, эти пространства надо подключить с помощью директивы using:

using System;
namespace HelloApp
    class Program 
    {
        static void Main(string[] args)
        {
            Console.WriteLine("hello");
        }
    }
}

Здесь подключается пространство имен System, в котором определен класс Console. Иначе нам бы пришлось писать полный путь к классу:

static void Main(string[] args)
{
    System.Console.WriteLine("hello");
}

Пространства имен могут быть определены внутри других пространств:

using HelloApp.AccountSpace;
namespace HelloApp
    class Program 
    {
        static void Main(string[] args)
        {
            Account account = new Account(4);
        }
    }
    namespace AccountSpace
    {
        class Account
        {
            public int Id { get; private set;}
            public Account(int _id)
            {
                Id = _id;
            }
        }
    }
}

В этом случае для подключения пространства указывается его полный путь с учетом внешних пространств имен: using HelloApp.AccountSpace;

Псевдонимы

Для различных классов мы можем использовать псевдонимы. Затем в программе вместо названия класса используется его псевдоним. Например, для вывода строки на экран применяется метод Console.WriteLine(). Но теперь зададим для класса Console псевдоним:


using printer = System.Console;
class Program
{
    static void Main(string[] args)
    {
        printer.WriteLine("Hello from C#");
        printer.Read();
    }
}

С помощью выражения using printer = System.Console указываем, что псевдонимом для класса System.Console будет имя printer. Это выражение не имеет ничего общего с подключением пространств имен в начале файла, хотя и использует оператор using. При этом используется полное имя класса с учетом пространства имен, в котором класс определен. И далее, чтобы вывести строку, применяется выражение printer.WriteLine("Hello from C#").
И еще пример. Определим класс и для него псевдоним:

using Person = HelloApp.User;
using Printer = System.Console;
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.name = "Tom";
            Printer.WriteLine(person.name);
            Printer.Read();
        }
    }
    class User
    {
        public string name;
    }
}

Класс называется User, но в программе для него используется псевдоним Person.
Начиная с версии C# 6.0 (Visual Studio 2015) в язык была добавлена возможность импорта функциональности классов. Например, опять же возьмем класс Console и применим новую функциональность:

using static System.Console;
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Hello from C# 6.0");
            Read();
        }
    }
}

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

using static System.Console;
using static System.Math;
using static HelloApp.Geometry;
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            double radius = 50;
            double result = GetArea(radius); //Geometry.GetArea
            WriteLine(result); //Console.WriteLine
            Read(); // Console.Read
        }
    }
    class Geometry
    {
        public static double GetArea(double radius)
        {
            return PI * radius * radius; // Math.PI
        }
    }
}

Типы данных и операторы

Прежде чем мы начнем что-то делать, нам нужно ввести базовый набор типов данных и операторов. Как уже упоминалось, C # - это статический сильно типизированный язык, поэтому нам нужно заботиться о типах данных. В итоге нам нужно будет указать тип какой-либо переменной.
Существует набор элементарных типов данных, который содержит следующие типы:


  • bool (1 байт), используемый для логических выражений (true, false)
  • char (2 байта), используемого для одиночного (unicode) символа
  • short (2 байта), используемого для хранения коротких целых чисел
  • int (4 байта), используемого для вычислений с целыми числами
  • long (8 байтов), используемых для вычислений с длинными целыми числами
  • float (4 байта), используемых для вычислений с числами с плавающей запятой с одиночной точностью
  • double (8 байтов), используемых для вычислений с числами с плавающей запятой двойной точности
  • decimal (16 байт), используемых для вычислений с фиксированными точками с плавающей запятой

Существуют также модифицированные типы данных, такие как unsigned integer ( uint), unsigned long ( ulong) и многие другие. Кроме того, был доставлен действительно хорошо работающий класс для работы со строками. Класс называется string.



В C # мы имеем тот же набор операторов (и более), что и в C:
  • Логические операторы , такие как ==!=<<=>>=!&&,||
  • Битовые операторы , как ^&|<<,>>
  • Арифметические операторы , такие как +-*/%++,--
  • Оператор присваивания = и комбинации оператора присваивания с бинарными операторами
  • Тернарный оператор ?:(inline condition) возвращает левую или правую сторону двоеточия в зависимости от условия, указанного перед вопросительным знаком
  • Скобки () для изменения иерархии операторов
Кроме того C # имеет некоторые встроенные-методы (определяется как унарные) , как typeofи sizeof и набор полезных операторов типа:
  • Стандартный оператор преобразования, ()как в C
  • Оператор приведения к типу as
  • Проверка соответствия типов is
  • Оператор объединения с NULL??
  • Оператор наследования :


Модификаторы out и ref

Ключевое out инициирует передачу аргументов по ссылке. Оно схоже с ключевым словом ref за исключением того, что при использовании ref перед передачей переменную необходимо инициализировать. Оно также похоже на ключевое слово in за исключением того, что in не позволяет вызываемому методу изменять значение аргумента. Для применения параметра out определение метода и метод вызова должны явно использовать ключевое слово out. Пример:

int initializeInMethod;
OutArgExample(
out initializeInMethod);
Console.WriteLine(initializeInMethod);    
// value is now 44
void OutArgExample(out int number){
    number =
44;
}


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


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


Тут  все почти то же самое, что и в C, поскольку мы имеем дело с синтаксисом C. При этом мы должны следовать следующим правилам:
  • Условия могут быть введены с помощью ifили switch.
  • Цикл возможноен при использовании forwhiledowhile.
  • Цикл возможно остановить, используя ключевое слово break (остановит самый внутренний цикл).
  • Цикл может пропустить остальное и вернуться к условию при помощи ключевого слова continue.
  • C# также имеет цикл foreach.
  • Другая возможность - печально известное goto - мы не будем обсуждать это.
  • Существуют и другие способы управления потоком программы, но мы представим их позже.
Следующий программный код представит несколько из упомянутых возможностей.
using System;

class Program
{
    static void Main(string[] args)
    {
        //We get some user input with the ReadLine() method
        string input = Console.ReadLine();

        //Let's see if this is empty or not
        if (input == "")
        {
            Console.WriteLine("The input is empty!");
        }
        else
        {
            //A string is a char array and has a Length property
            //It also can be accessed like a char array, giving
            //us single chars.
            for (int i = 0; i < input.Length; i++)
            {
                switch (input[i])
                {
                    case 'a':
                        Console.Write("An a - and not ... ");
                        //This is always required, we cannot just fall through.
                        goto case 'z';
                    case 'z':
                        Console.WriteLine("A z!");
                        break;
                    default:
                        Console.WriteLine("Whatever ...");
                        //This is required even in the last block
                        break;
                }
            }
        }
    }
}


Вызов цикла foreach недоступен в C. В С# же можно использовать foreach с каждым типом, который определяет какой-то итератор. Мы увидим, что это значит позже. Пока нам нужно только знать, что каждый массив уже определяет такой итератор. Следующий фрагмент кода будет использовать foreach-цикл для вывода каждого элемента массива.
//Creating an array is possible by just appending [] to any data type
int[] myints = new int[4];
//This is now a fixed array with 4 elements. Arrays in C# are 0-based
//hence the first element has index 0.
myints[0] = 2;
myints[1] = 3;
myints[2] = 17;
myints[3] = 24;

//This foreach construct is new in C# (compared to C++):
foreach(int myint in myints)
{
    //Write will not start a new line after the string.
    Console.Write("The element is given by ");
    //WriteLine will do that.
    Console.WriteLine(myint);
}


Есть некоторые ограничения использования foreach по сравнению с forПрежде всего, это не так эффективно, как for, еще одна операция для запуска цикла и всегда вызов Next метода итераторов в конце каждой итерации. Во-вторых, мы не можем изменить текущий элемент. Это связано с тем, что foreach работает на итераторах, которые в общем неизменяемы, т. е. один элемент не может быть изменен. Это также необходимо для обеспечения последовательной итерации.

Популярные сообщения из этого блога

Аппроксимация функций рядом Тейлора

Основные структуры данных: Множества

Doxygen, оформление документации