Общая структура программы C#
Программа на языке C# состоит из одного или нескольких файлов. Каждый файл может содержать или не содержать пространства имен. Пространство имен может содержать типы, такие как классы, структуры, интерфейсы, перечисления и делегаты или другие пространства имен. Ниже приведен пример структуры программы на C#, содержащей все эти элементы.
// A skeleton of a C# program using System; // Your program starts here: Console.WriteLine("Hello world!"); namespace YourNamespace < class YourClass < >struct YourStruct < >interface IYourInterface < >delegate int YourDelegate(); enum YourEnum < >namespace YourNestedNamespace < struct YourStruct < >> >
В предыдущем примере для точки входа программы используются инструкции верхнего уровня. Эта функция была добавлена в C# 9. До версии C# 9 точка входа была статическим методом с именем Main , как показано в следующем примере:
// A skeleton of a C# program using System; namespace YourNamespace < class YourClass < >struct YourStruct < >interface IYourInterface < >delegate int YourDelegate(); enum YourEnum < >namespace YourNestedNamespace < struct YourStruct < >> class Program < static void Main(string[] args) < //Your program starts here. Console.WriteLine("Hello world!"); >> >
Связанные разделы
Сведения об этих элементах программы см. в разделе о типах в руководстве по основам:
Спецификация языка C#
Дополнительные сведения см. в разделе Основные понятия в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
Class program c что это
Класс — это тип, создаваемый пользователем. Классы являются основным строительным материалом программ, написанных на языке C#. Если в C++ классы используют только в случае необходимости (их можно и вообще не применять!), то в C# всё построено на классах . Даже простейшая программа типа «Здравствуй, мир!» содержит в себе один класс. Обычно же программы на C# состоят из большого количества пользовательских классов.
Каждый класс определяет какую-нибудь сущность. При проектировании программы необходимо представить решаемую задачу в виде совокупности неких объектов, каждый из которых должен обладать необходимыми характеристиками. Совершено очевидно, что необходимо реализовать классы, в которые заложены эти возможности, а затем создавать и использовать в тексте программы объекты этих классов.
Понятие класса является основой концепции объектно-ориентированного программирования (ООП) .
ООП базируется на т рёх основных принципах : инкапсуляция, наследование и полиморфизм.
Инкапсуляция — это объединение под одним общим именем в виде класса данных (их часто называют полями) и методов по обработки этих данных. Вне класса по-умолчанию все поля и методы просто недоступны. Делается это с целью исключения ошибочного изменения полей «чужими» методами.
Именно благодаря такому искусственному сокрытию данных (и часто — методов) появляется возможность разработки больших программных комплексов (на миллионы строк кода), которые могут быть вполне работоспособны.
Принцип наследования — второй по значимости среди основных идей ООП. Благодаря наследованию мы можем на основе одного класса (класса-предка) спроектировать другой класс (класс-наследник), который унаследует от своего предка всё, что нам потребуется. Естественно, в классе-наследнике появятся какие-то новые свойства (поля, методы), которые расширят возможности класса-наследника. Но при этом нам не требуется заново разрабатывать тот программный код, который был в классе-предке. Понятно, что принцип наследования может существенно сократить сроки разработки новых программ и повысить их надёжность.
Термин полиморфизм переводится как многообразие форм. К элементам полиморфизма можно отнести перегрузку методов, перегрузку операции, использование виртуальных методов.
Принцип полиморфизма позволяет в ряде случаев заметно упростить проектирование программ, повысить её наглядность. Всё это также способствует повышению надёжности программного обеспечения и сокращению сроков его разработки.
Рассмотрев вкратце основные идеи ООП, далее перейдём к более подробному их изучению. И начнём с главного — как создавать и использовать классы.
Структура класса и порядок его использования
Формально любой класс можно описать таким образом:
атрибуты class имя_класса : предки
атрибуты — одно из ключевых слов, определяющих доступность класса (например, public , protected , private ), либо какие-то другие базовые свойства класса ( static — статичный класс, abstract — абстрактный класс, sealed — бесплодный класс);
имя_класса — задаётся по общим правилам (как для любого идентификатора, например, как для имени переменной);
предки — необязательный параметр. Класс, который мы создаём, может быть наследником какого-то другого класса (но только одного!) и наследником любого количества интерфейсов (об интерфейсах речь пойдёт далее);
члены_класса — это данные, инкапсулированные в классе, и методы по их обработке.
Для того, чтобы использовать разработанный класс, необходимо где-то в программе создать объект этого класса и вызвать его метод:
имя_класса переменная = new имя_класса () ;
Здесь с помощью операции new создаётся объект нашего класса. В скобках как после имени класса, так и после названия метода возможны какие-либо параметры (если это определено в классе).
Вызов метода возможен только с использованием операции доступа (операция «точка». Указываем название переменной, имеющей тип данного класса, затем ставим точку и только потом пишем имя метода с возможными фактическими параметрами.
Пример простого класса
Для большей ясности сразу же рассмотрим пример с небольшим классом.
Пример . Создать класс, моделирующий работу с треугольником (задаются три стороны a , b и c и вычисляется площадь треугольника s ).
Возможная реализация программы:
double a, b, c; // стороны треугольника
// метод для ввода данных
Class program c что это
Точкой входа в программу на языке C# является метод Main. Именно с этого метода начинается выполнение программы на C#. И программа на C# должна обязательно иметь метод Main . Однако может возникнуть вопрос, какой еще метод Main, если, например, Visual Studio 2022 по умолчанию создает проект консольного приложения со следующим кодом:
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!");
И эта программа никаких методов Main не содержит, но при этом нормально выполняется и выводит на консоль строку «Hello, World!», как и запланировано. Это так называемая программа верхнего уровня (top-level program). А вызов Console.WriteLine(«Hello, World!») представляет инструкцию вехнего уровня (top-level statement)
Однако в реальности этот код неявно помещается компилятором в метод Main, который, в свою очередь, помещается в класс Program. В действительности название класса может быть любым (как правило, это класс Program, собственно поэтому генерируемый по умолчанию файл кода называется Program.cs ). Но метод Main является обязательной частью консольного приложения. Поэтому выше представленный код фактически эквивалентен следующей программе:
class Program < static void Main(string[] args) < // See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!"); >>
Определение метода Main обязательно начинается с модификатора static , которое указывает, что метод Main — статический. Позже мы подробнее разберем, что все это значит.
Возвращаемым типом метода Main обязательно является тип void . Кроме того, в качестве параметра он принимает массив строк — string[] args — в реальной программе это те параметры, через которые при запуске программы из консоли мы можем передать ей некоторые значения. Внутри метода располагаются действия, которые выполняет программа.
До Visual Studio 2022 все предыдущие студии создавали по умолчанию примерно такой код. Но начиная с Visual Studio 2022 нам необязательно вручную определять класс Program и в нем метод Main — компилятор генерирует их самостоятельно.
Если мы определяем какие-то переменные, константы, методы и обращаемся к ним, они помещаются в метод Main. Например, следующая программа верхнего уровня
string hello = «Hello METANIT.COM»; Print(hello); void Print(string message)
будет аналогична следующей программе:
class Program < static void Main(string[] args) < string hello = "Hello METANIT.COM"; Print(hello); void Print(string message) < Console.WriteLine(message); >> >
Если определяются новые типы, например, классы, то они помещаются вне класса Program. Например, код:
Person tom = new(); tom.SayHello(); class Person < public void SayHello() =>Console.WriteLine("Hello"); >
будет аналогичен следующему
class Program < static void Main(string[] args) < Person tom = new(); tom.SayHello(); >> class Person < public void SayHello() =>Console.WriteLine("Hello"); >
Однако надо учитывать, что опредления типов (в частности классов) должны идти в конце файла после инструкций верхнего уровня. То есть:
// инструкции верхнего уровня (top-level statements) Person tom = new(); tom.SayHello(); // определение класса идет после инструкций верхнего уровня class Person < public void SayHello() =>Console.WriteLine("Hello"); >
Таким образом, мы можем продолжать писать программы верхнего уровня без явного определения метода Main. Либо мы можем явным образом определить метод Main и класс Program:

И этот код будет выполняться аналогичным образом, как если бы мы не использовали класс Program и метод Main.
Объекты — создание экземпляров типов
Определение класса или структуры подобно чертежу, на котором указаны действия, выполняемые типом. В сущности, объект является блоком памяти, выделенной и настроенной в соответствии с чертежом. Программа может создать множество объектов одного класса. Объекты также называют экземплярами. Они могут храниться либо в именованной переменной, либо в массиве или коллекции. Клиентский код — это код, использующий эти переменные для вызова методов и доступа к открытым свойствам объекта. В объектно-ориентированном языке, таком как C#, стандартная программа состоит из нескольких динамически взаимодействующих объектов.
Поведение статических типов отличается от описанного здесь поведения. Дополнительные сведения см. в статье Статические классы и члены статических классов.
Экземпляры структуры и Экземпляры классов
Так как классы являются ссылочными типами, в переменной объекта класса хранится ссылка на адрес объекта в управляемой куче. Если первой переменной назначена вторая переменная того же типа, то обе переменные ссылаются на объект по указанному адресу. Этот момент более подробно рассматривается далее в этой статье.
Экземпляры классов создаются с помощью new оператора . В приведенном ниже примере Person является типом, а person1 и person2 — экземплярами или объектами этого типа.
using System; public class Person < public string Name < get; set; >public int Age < get; set; >public Person(string name, int age) < Name = name; Age = age; >// Other properties, methods, events. > class Program < static void Main() < Person person1 = new Person("Leopold", 6); Console.WriteLine("person1 Name = Age = ", person1.Name, person1.Age); // Declare new person, assign person1 to it. Person person2 = person1; // Change the name of person2, and person1 also changes. person2.Name = "Molly"; person2.Age = 16; Console.WriteLine("person2 Name = Age = ", person2.Name, person2.Age); Console.WriteLine("person1 Name = Age = ", person1.Name, person1.Age); > > /* Output: person1 Name = Leopold Age = 6 person2 Name = Molly Age = 16 person1 Name = Molly Age = 16 */
Так как структуры являются типами значений, в переменной объекта структуры хранится копия всего объекта. Экземпляры структур также можно создать с помощью new оператора , но это необязательно, как показано в следующем примере:
using System; namespace Example < public struct Person < public string Name; public int Age; public Person(string name, int age) < Name = name; Age = age; >> public class Application < static void Main() < // Create struct instance and initialize by using "new". // Memory is allocated on thread stack. Person p1 = new Person("Alex", 9); Console.WriteLine("p1 Name = Age = ", p1.Name, p1.Age); // Create new struct object. Note that struct can be initialized // without using "new". Person p2 = p1; // Assign values to p2 members. p2.Name = "Spencer"; p2.Age = 7; Console.WriteLine("p2 Name = Age = ", p2.Name, p2.Age); // p1 values remain unchanged because p2 is copy. Console.WriteLine("p1 Name = Age = ", p1.Name, p1.Age); > > /* Output: p1 Name = Alex Age = 9 p2 Name = Spencer Age = 7 p1 Name = Alex Age = 9 */ >
Память для p1 и p2 выделена в стеке потока. Эта память освобождается вместе с типом или методом, в котором она объявлена. Эта одна из причин того, почему структуры копируются при присваивании. Напротив, при выходе всех ссылок на объект из области действия среда CLR автоматически освобождает память (выполняет сборку мусора), выделенную для экземпляра класса. Детерминированное уничтожение объекта класса, как в C++, невозможно. Дополнительные сведения о сборке мусора в .NET см. в статье Сборка мусора.
В среде CLR процесс выделения и освобождения памяти в управляемой куче значительно оптимизирован. В большинстве случаев нет существенной разницы в затратах производительности на выделение экземпляра класса в куче и выделение экземпляра структуры в стеке.
Идентификация объектов и равенство значений
Сравнивая два объекта на предмет равенства, сначала необходимо определить, нужно ли узнать, представляют ли две переменные один объект в памяти или значения одного или нескольких их полей являются равными. Если вы планируете сравнивать значения, необходимо учитывать, являются ли объекты экземплярами типов значений (структуры) или ссылочными типами (классы, делегаты, массивы).
- Чтобы определить, ссылаются ли два экземпляра класса на одно расположение в памяти (то есть имеют одинаковый идентификатор), воспользуйтесь статическим методом Object.Equals. (System.Object является неявным базовым классом для всех типов значений и ссылочных типов, включая структуры и классы, определенные пользователем.)
- Чтобы определить, имеют ли поля экземпляра в двух экземплярах структуры одинаковые значения, воспользуйтесь методом ValueType.Equals. Так как все структуры неявно наследуются от System.ValueType, метод можно вызвать непосредственно в объекте, как показано в следующем примере:
// Person is defined in the previous example. //public struct Person // < // public string Name; // public int Age; // public Person(string name, int age) // < // Name = name; // Age = age; // >//> Person p1 = new Person("Wallace", 75); Person p2 = new Person("", 42); p2.Name = "Wallace"; p2.Age = 75; if (p2.Equals(p1)) Console.WriteLine("p2 and p1 have the same values."); // Output: p2 and p1 have the same values.
Связанные разделы
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.