В упрощенном виде разработка и выполнение приложений .NET состоит из следующих этапов:
deploy ) или установка сборки на предназначенную платформуCLR - это среда исполнения промежуточного кода MSIL. При загрузке приложения на выполнение она загружается в память первой и в последующем управляет работой приложения. В неуправляемом коде готовый машинный код сразу исполняется операционной системой. CLR же - это программа, которая включает в себя целый набор средств: JIT -компилятор, Manager Memory, Garbage Collector и другие. Все они, как посредники между приложением и операционной системой, обеспечивают выполнение приложения.
Чтобы проделать всю эту работу, необходимо задействовать несколько компонентов платформы .NET Framework, которые представлены на рисунке

Спецификация общего языка ( CLS - Common Language Specification ) - это набор правил для всех .NET - совместимых языков для успешной их работы в платформе .NET Framework.
Составляя программу на C#, программист на понятном ему и компилятору языке составляет инструкции для CLR, которые после компиляции будут преобразованы в байт-код среды CLR.
Среда CLR обеспечивает выполнение приложений .NET, а также предоставляет службы, необходимые этим приложениям во время выполнения. При обращении среда CLR сама выполняет все необходимые операции, включая компиляцию, распределение памяти и управление кодом обратившегося к ней приложения.
Среда выполнения CLR работает с промежуточным представлением исходного кода, написанного на любом .NET -совместимом языке, удовлетворяющем спецификации CLS.
PE-файл состоит из 4-х частей
Несмотря на то, что управляемые модули являются переносимыми исполняемыми файлами, оно не могут быть запущены автономно, сначала их необходимо включить в сборку и передать на выполнение среде CLR, т.е. в операционной системе должен быть движок CLR.
Язык MSIL ( IL ) является промежуточным

Сборки ( Assembly ) - развертываемый блок приложений .NET, содержащий в себе все необходимое для нормальной работы. Любое приложение .NET является сборкой. Сборки могут содержать один или несколько
| Псевдоним C# | Соответствие CLS | Системный тип | Диапазон | Пояснения |
|---|---|---|---|---|
void |
+ | Void |
Пусто | Применяется для соблюдения синтаксиса |
sbyte |
- | SByte |
-128 —127 | Знаковое 8 бит |
byte |
+ | Byte |
0 —255 | Беззнаковое 8 бит |
short |
+ | Int16 |
-32'768 —32'767 | Знаковое 16 бит |
ushort |
- | UInt16 |
0 —65'535 | Беззнаковое 16 бит |
int |
+ | Int32 |
-2'147'483'648 —2'147'483'647 | Знаковое 32 бит (4 байта) |
uint |
- | UInt32 |
0 —4'294'967'295 | Беззнаковое 32 бит |
long |
+ | Int64 |
-9'223'372'036'854'775'808 —9'223'372'036'854'775'807 | Знаковое 64 бит (8 байт) |
ulong |
- | UInt64 |
0 —18'446'774'073'709'551'615 | Беззнаковое 64 бит (8 байт) |
char |
+ | Char |
U+0000 —U+FFFF | Для описания только одного символа Unicode 16 бит |
float |
+ | Single |
1.5x10-45 —3.4x1038 | Знаковое с плавающей точкой 32 бит (4 байта) |
double |
+ | Double |
5.0x10-324— 1.7x10308 | Знаковое с плавающей точкой 64 бит (8 байт) |
bool |
+ | Boolean |
true или false |
Логическое |
decimal |
+ | Decimal |
100 —1028 | Знаковое 96 бит (12 байт) |
string |
+ | String |
Ограничено только системной памятью | Для описания строки символов Unicode любой длины |
object |
+ | Object |
Практически все что угодно. Все типы происходят от System.Object, поэтому объектом является все |
Класс, базовый для всех типов в .NET |
Полезное средство для исследования сборок - утилита ILDasm ( ILDasm - Intermediate Language Disassembler utility ), Ее вызов можно подключить к оболочке Visual Studio 2005 с помощью команды:
Tools/External Tools...

Утилита находится в
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe
После запуска утилиты в ее меню File/Open можно открыть нужную сборку, например, системную
C:\WINNT\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

Для просмотра метаданных типов достаточно загрузить сборку в ILDasm и нажать клавиши Ctrl-M. Команда меню Dump TreeView утилиты выгружает иерархию исследуемой сборки в текстовый файл. Графические значки будут заменены соответствующими текстовыми аббревиатурами.
Через панель Solution Explorer/References подключим сборку system.windows.forms.dll
Создайте новый проект 
Если открыть панель Object Browser, то можно увидеть подключенные библиотечные сборки, которые оболочка посчитала (по мнению ее разработчиков) необходимым присоединить к заготовке проекта

Удалим ссылки на сборки System.Data и System.Xml, которые нужны при работе с базами данных и языком XML. Через панель Object Browser это сделать нельзя (она только для просмотра), только в панели Solution Explorer.
В панели Solution Explorer раскройте узел References, выделите обе сборки System.Data и System.Xml, затем командой Remove контекстного меню удалите их из проекта
Командой Project/Add Reference откройте окно Add Reference и добавьте в проект ссылку на библиотечную сборку System.Windows.Forms
Каждая сборка содержит множество различных пространств имен, которые нужны для однозначной адресации к типам, имеющим возможно одинаковые имена. Добавленную сборку System.Windows.Forms мы будем использовать для создания графического окна Windows в нашем
Через панель Solution Explorer откройте файл-заготовку Program.cs и откорректируйте его следующим образом// Подключение пространств имен
// для сокращенной адресации классов
using System;
using System.Windows.Forms;
// Пространство имен в проекте можно и не объявлять
// Тогда будет использовано глобальное пространство имен
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консольного окна сборки mscorlib.dll
Console.Title = "Это консольное окно";
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WindowWidth = 50; // Ширина в знакоместах
Console.WindowHeight = 10; // Высота в текстовых строках
Console.CursorVisible = false;
Console.WriteLine("Простая C# - программа");
// Графическое окно сборки System.Windows.Forms.dll
MessageBox.Show("Привет из графического окна сообщений!!!");
Console.ReadLine();
}
}
}
Результат выполнения приложения будет таким
abstract |
as |
base |
bool |
break |
byte |
case |
catch |
char |
checked |
class |
const |
continue |
decimal |
default |
delegate |
do |
double |
else |
enum |
event |
explicit |
extern |
false |
finally |
fixed |
float |
for |
foreach |
goto |
if |
|
in |
int |
interface |
internal |
is |
lock |
long |
namespace |
new |
null |
object |
operator |
out |
override |
params |
private |
protected |
public |
readonly |
ref |
return |
sbyte |
sealed |
short |
sizeof |
stackalloc |
static |
string |
struct |
switch |
this |
throw |
true |
try |
typeof |
uint |
ulong |
|
|
ushort |
using |
virtual |
void |
|
while |
Разработка кода программы C# сводится к последовательному развитию типов, главным из которых является класс. Каждый класс рекомендуется размещать, как самостоятельный фрагмент кода программы, в отдельном файле с расширением *.cs. Но и в одном файле можно размещать сколько угодно классов, просто неудобно в них разбираться.
Внутри класса дается объявление-реализация методов, определяющих его функциональность. Каждый класс размещается внутри именованных скобок namespace Имя{...}, задающих простанство имен, хотя это и необязательно - тогда будет использовано глобальное пространство имен. Кроме того, вначале файла с пользовательским классом перечисляются пространства имен используемых библиотечных классов для сокращенного их вызова. Объявление класса не должно заканчиваться точкой_с_запятой, как это принято в C++.
Аргументами метода Main() являются Main()
static void Main(string[] args) {...}
|
|---|
public static void Main(string[] args) {...} |
public static void Main() {...} |
private static void Main(string[] args) {...} |
private static void Main() {...} |
static int Main(string[] args) {... return 0; } |
public static int Main(string[] args) {... return 0; } |
public static int Main() {... return 0; } |
private static int Main(string[] args) {... return 0; } |
private static int Main() {... return 0; } |
Главное требование - функция Main() должна быть статической, поскольку запускается тогда, когда еще не существует ни одного экземпляра класса. А объявлена она общедоступной или приватной - система ее всегда найдет.
Вернемся к медоту Main() нашего приложения, вернее, к его заголовку.
Скорректируйте файл Program.cs, чтобы он выглядел такusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Простая C# - программа");
Console.ReadLine();// Для задержки окна
}
}
}
Измените метод Main() так, чтобы внутри него перебирались using System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine("Arg: {0}", args[i]);
}
Console.ReadLine();// Для задержки окна
}
}
}
При запуске приложения никаких параметров не будет, поскольку мы их не задали.
Необходимо выполнить следующее:
Windows через меню Пуск/ ВыполнитьОбзор найти исполнимую сборку проектаНапример, командная строка
C:\Tmp\Test.exe -arg1 -arg2 "-Привет семье" -345
при запуске нашего последнего примера даст результат
Теперь то же самое выполним, используя окно командной строки оболочки. Это окно позволяет выполнить команды непосредственно в интегрированной среде разработки. Через него можно выполнить любую команду меню и даже те команды, которые не предусмотрены ни на каком меню.
Чтобы отобразить окно команд, выполните опцию меню View/Windows Other/Command Window
Введите в окне команд после промптера строку, состоящую из вызова процессора команд ( Shell ), полного пути нахождения нашей сборки, имени сборки и аргументов, разделенных символами пробела. У меня это будет выглядеть так
Если путь содержит составные имена папок, разделенные пробелами, то вызов нужно заключать в двойные кавычки, например

После завершения команды нажатием клавиши Enter запустится консольное окно со следующим результатом
Нажмите клавишу Enter, чтобы прервать работу Перебор введенных ранее команд в окне Command Window выполняется стрелками "вверх/вниз" клавиатуры .
Для задания аргументов в режиме Debug сделайте следующее:
В панели Solution Explorer щелкните на пиктограмме Properties
То же самое можно сделать, если выполнить команду меню Project/MyApp Properties или для узла проекта вызвать контекстное меню и выбрать опцию Properties

Настройте окно свойств проекта для вкладки Debug так
Выполните команду Debug/Start Debugging или щелкните на одноименной кнопке окна оболочкиПолучим результат
Замените в методе Main() цикл for на foreach, получив такой кодusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
foreach (string arg in args)
{
Console.WriteLine("Arg: {0}", arg);
}
Console.ReadLine();// Для задержки окна
}
}
}
Постройте приложение - результат будет прежнимИзвлечение параметров методом Environment.GetCommandLineArgs()
using System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
// Аргументы командной строки
// извлекаются классом Environment
string[] theArgs = Environment.GetCommandLineArgs();
Console.WriteLine("Путь к приложению: {0}", theArgs[0]);
for (int i = 1; i < theArgs.Length; i++)
Console.WriteLine("Аргумент: {0} ", theArgs[i]);
Console.ReadLine();// Для задержки окна
}
}
}
Постройте приложение - результат будет прежнимВсе объекты в C# создаются на куче и с помощью оператора new. Инициализация объектов при их создании выполняется функциями-членами класса, которые называются конструкторами. Конструкторы не нужно вызывать специально. Для статических классов существует только один статический конструктор, который не должен иметь аргументов. Они вызываются автоматически при создании объекта и только один раз. Повторно вызвать конструктор как метод для уже существующего объекта невозможно.
Для нестатических классов различают конструкторы по умолчанию и общие конструкторы. Конструктор по умолчанию не имеет аргументов. Программист может его определить сам или система предоставит его автоматически, если программистом не был объявлен ни один конструктор. Конструктор по умолчанию может быть у класса только один. Общие конструкторы имеют аргументы. Класс может иметь несколько общих конструкторов, только они должны различаться сигнатурой (количеством, типом и порядком следования аргументов), чтобы компилятор мог однозначно определить перегружаемый конструктор по сигнатуре в фактическом вызове.
Язык C# имеет существенное отличие от C++ в отношении конструктора по умолчанию. В C++ если программист не предусмотрел свой конструктор по умолчанию, то предоставляемый системой конструктор ничего с данными-членами класса не делает и они остаются неинициализированными. В C# конструктор по умолчанию, предоставляемый системой, производит инициализацию данных, большей частью нулем. Но если программист определил хоть один конструктор с параметрами, то конструктор по умолчанию системой уже не предоставляется и если он используется, то нужно определить свой конструктор по умолчанию, хоть с пустым телом.
Имена конструкторов должны совпадать с именем класса. Конструкторы должны имет
Для иллюстрации сказанного продолжим модификацию нашего проекта MyClass.
Модифицируйте класс следующим образомusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Внутренние переменные - поля
private int x, y;
// Конструктор по умолчанию
public MyClass()
{
Console.WriteLine("Конструктор по умолчанию");
}
// Конструктор с параметрами
public MyClass(int a, int b)
{
Console.WriteLine("Конструктор с параметрами");
x = a;
y = b;
}
// Сервисы класса
public int GetX() { return x; }
public int GetY() { return y; }
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем два объекта-экземпляра класса
MyClass ob1 = new MyClass(); // Вызов конструктора по умолчанию
Console.WriteLine("ob1: x = {0}; y = {1} \n", ob1.GetX(), ob1.GetY());
MyClass ob2 = new MyClass(10, 15); // Вызов параметризованного конструктора
Console.WriteLine("ob2: x = {0}; y = {1}", ob2.GetX(), ob2.GetY());
Console.Read();
}
}
}
Постройте проект, чтобы получить результат
Мы видим, что поля x и y в первом объекте не остались брошенными, как это бы было в C++, а инициализировались нулями в соответствии с их типом. Такой порядок инициализации по умолчанию смысловым типом не относится к неинициализированным ссылкам. Их нужно обязательно инициализировать нулевым адресом с помощью ключевого слова null.
Деструкторы в C# применять не нужно, хотя они и предусмотрены синтаксисом. Программист не должен заботиться об освобождении затребованной у системы памяти на куче. Об этом позаботиться специальный механизм, который называется сборщиком мусора ( GC - Garbage Collector ). Сборщик мусора платформы .NET освободит память автоматически.
В последнем примере мы внутри класса MyClass в методе Main() создавали объекты того же самого класса. Это вносит лишнюю путаницу в изучение кода. Функция Main() никакого отношения к классу MyClass не имеет и присутствует там только из-за того, что глобальные функции в C# недопустимы и ее нужно куда-то приткнуть. Вынесем ее в отдельный класс, чтобы отделить "котлеты от мух".
Вначале заменим сервисные методы доступа к закрытым полям класса на общедоступные свойства.
Измените код файла MyClass.cs так, как показано нижеusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass(10, 15);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
Операторы get и set называются аксессорами, а ключевое слово value способно иметь значение любого типа. Свойство можно объявить "только для чтения", используя ключевое слово readonly, а можно просто убрать блок аксессора set.
Запустите приложение, чтобы получить результат
Теперь обращение к внутренним полям класса осуществляется через общедоступные свойства.
Упакуем функцию Main() в отдельный класс с именем EntryPoint, для этого
Выделите код функции Main() и выполните для выделенного фрагмента опцию контекстного меню Surround With (охватить с помощью)
В появившемся всплывающем меню выберите тип охватывающего блока classОболочка создаст MyClass. public, т.е. по нему можно создавать объекты только внутри внешнего класса. Кроме того, синтаксис C# не допускает в одном пространстве имен существование типов с одинаковыми именами.
Переименуйте новый класс в EntryPoint и запустите приложениеМы видим, что функция Main() доступна и из внутреннего класса.
Вынесите класс EntryPoint в пространство имен проекта и запустите приложение. Все остается прежним!
Для структурирования отступов в коде уничтожте последнюю закрывающую фигурную скобку пространства имен и вновь ее создайте. Код будет выровнен!Мы в данном примере нехорошо поступили (ну, почти нехорошо), потому что принято каждый класс размещать в отдельном файле с названием, обычно совпадающем с именем класса.
Разработчики оболочки предполагали, что в файле Program.cs будет находиться функция Main(), а пользовательский код желательно (но не обязательно) размещать в других файлах. Согласимся с ними.
Выберите опцию главного меню Project/Add New Item и заполните диалоговое окно мастера так
Перенесите в новый файл содержимое класса MyClassПосле этого коды файлов будут такими
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass(10, 15);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
using System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
В C++ в классе можно простые переменные только объявлять, а инициализировать их нужно в конструкторе или устанавливать путем присваивания функциями Set() из клиента. В C# такое ограничение отсутствует и данные-члены класса можно инициализировать при их объявлении в классе. Примеры
Измените код класса MyClass нашего примера так, как показано нижеusing System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x = 10, y = 15;
// Конструктор по умолчанию
public MyClass() { }
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass();
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
// Создаем еще один экземпляр,
// используя ту же ссылочную переменную
// Прежний объект будет утерян
ob = new MyClass(100, 150);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
Обратите внимание, что при создании объекта класса MyClass используется одна и та же ссылочная переменная, после присвоения которой нового значения прежний брошенный объект становится добычей сборщика мусора.
После построения проекта получим результат

Отметим, что подобная прямая инициализация членов-данных в структурах недопустима. Перегрузка конструктора по умолчанию в структурах тоже недопустима. Чтобы убедиться в этом, объявите MyClass как структуру.
В C# различают два вида типов: значимые ( value-based ) и ссылочные ( reference-based ). Переменная значимого типа представляет закрепленное за ней само значение оперативной памяти. Переменная ссылочного типа содержит лишь адрес области оперативной памяти с размещаемым в этой области значением. Все структурные переменные размещаются на стеке, а ссылочные - на управляемой куче ( managed heap ). К структурным типам относятся перечисления и структуры, а к ссылочным - классы, делегаты, интерфейсы.
Такое разделение принято для экономии памяти и быстродействия.
Когда выполняется присваивание одной значимой переменной другой, то создается побитовая копия данных, которые размещены в разных областях стековой памяти и существуют независимо друг от друга. При выполнении присваивания ссылочных переменных создается копия адреса, после чего обе переменные будут ссылаться на одну и ту же область памяти на управляемой куче. То есть создается как-бы псевдонимы одной и той же области памяти. Это означает, что изменения объекта по одной ссылочной переменной равноценно изменениям по псевдониму
Добавьте к нашему проекту файл с именем MyStruct.c s типа C #, выполнив команду Project/Add Clas s, и скопируйте в него код из файла MyClass.cs. Исправьте программу, чтобы она была такойusing System;
namespace MyApp
{
struct MyStruct
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyStruct(int x, int y)
{
this.x = x;
this.y = y;
}
// Конструктор по умолчанию перегружать нельзя
// public MyStruct() { }
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Конструктор по умолчанию
public MyClass() { }
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем две структуры
MyStruct struct1 = new MyStruct(10, 15);
MyStruct struct2 = new MyStruct();
// Создаем два объекта
MyClass object1 = new MyClass(10, 15);
MyClass object2 = new MyClass();
// Печатаем
Console.WriteLine("Структуры до изменений:");
Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
Console.WriteLine("\nОбъекты до изменений:");
Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);
// Присваиваем значения
struct1 = struct2;// Копируются значения, struct1 существует
object1 = object2; // Копируется адрес, object1 теряется
// Изменяем экземпляры
struct2.X = 20; struct2.Y = 30;
object2.X = 20; object2.Y = 30;
// Печатаем
Console.WriteLine("\nСтруктуры после изменений:");
Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
Console.WriteLine("\nОбъект после изменений:");
Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);
Console.ReadLine();
}
}
}
Результат выполнения будет таким

При копировании переменных-структур копируются значения прикрепленных структур, а при копировании переменных-объектов копируются значения адресов. Структуры остаются адресуемыми по своим переменным, только приобретают равные значения данных. При смене значения ссылки на объект этот объект теряется для программы, а обе ссылки начинают адресовать один и тот же объект в памяти, созданный динамически на управляемой куче.
Поставим некоторые предварительные вопросы и ответы на них в таблице
| Вопрос | Значимые типы | Ссылочные типы |
|---|---|---|
| Место размещения | Стек | Управляемая куча |
| Как будет представлена переменная, которой в качестве значения присвоен этот тип | В виде локальной побитовой копии типа | В виде указателя на область оперативной памяти, относящейся к объекту этого типа |
| Что может являться для них базовым типом | Эти типы могут производиться только напрямую от типа System.ValueType |
Могут производиться от любого другого типа, кроме System.ValueType, если этот тип не является закрытым |
| Может ли этот тип выступать производителем другого типа | Нет. |
Да, если этот ссылочный тип не определен как закрытый |
| Как производится передача параметров этого типа в функцию | Только по значению, т.е. вызывающей функции передаются локальные копии значений переменных | Передаются как ссылки, т.е. вызывающей функции передаются адреса размещения соответствующих объектов в оперативной памяти |
Существует ли возможность переопределить встроенный метод Object.Finalize() |
Нет. |
Да, но не напрямую |
| Можно ли определить конструкторы для этого типа | Да, но конструктор по умолчанию зарезервирован, поэтому все определяемые конструкторы должны иметь параметры | Да, сколько угодно, лишь бы они различались сигнатурой |
| Когда происходит разрушение переменной данного типа | Когда происходит выход из области видимости | Во время процесса сборки мусора в управляемой куче с помощью механизма (сборщик мусора) |
| Можно ли напрямую инициализировать данные при объявлении типа | Нет | Да |
| Можно ли перегружать конструктор по умолчанию | Нет, он зарезервирован за системой | Да, и обязательно, если определен хоть один конструктор с параметрами |
Несмотря на все различия, значимые и ссылочные типы могут иметь и поддерживать как встроенные (стандартные), так и определенные пользователем (программистом) методы, свойства, события в необходимом количестве. Но удобно за значимыми типами закреплять только поддержку сложных данных.
В упрощенном виде разработка и выполнение приложений .NET состоит из следующих этапов:
deploy ) или установка сборки на предназначенную платформуCLR - это среда исполнения промежуточного кода MSIL. При загрузке приложения на выполнение она загружается в память первой и в последующем управляет работой приложения. В неуправляемом коде готовый машинный код сразу исполняется операционной системой. CLR же - это программа, которая включает в себя целый набор средств: JIT -компилятор, Manager Memory, Garbage Collector и другие. Все они, как посредники между приложением и операционной системой, обеспечивают выполнение приложения.
Чтобы проделать всю эту работу, необходимо задействовать несколько компонентов платформы .NET Framework, которые представлены на рисунке

Спецификация общего языка ( CLS - Common Language Specification ) - это набор правил для всех .NET - совместимых языков для успешной их работы в платформе .NET Framework.
Составляя программу на C#, программист на понятном ему и компилятору языке составляет инструкции для CLR, которые после компиляции будут преобразованы в байт-код среды CLR.
Среда CLR обеспечивает выполнение приложений .NET, а также предоставляет службы, необходимые этим приложениям во время выполнения. При обращении среда CLR сама выполняет все необходимые операции, включая компиляцию, распределение памяти и управление кодом обратившегося к ней приложения.
Среда выполнения CLR работает с промежуточным представлением исходного кода, написанного на любом .NET -совместимом языке, удовлетворяющем спецификации CLS.
PE-файл состоит из 4-х частей
Несмотря на то, что управляемые модули являются переносимыми исполняемыми файлами, оно не могут быть запущены автономно, сначала их необходимо включить в сборку и передать на выполнение среде CLR, т.е. в операционной системе должен быть движок CLR.
Язык MSIL ( IL ) является промежуточным

Сборки ( Assembly ) - развертываемый блок приложений .NET, содержащий в себе все необходимое для нормальной работы. Любое приложение .NET является сборкой. Сборки могут содержать один или несколько
| Псевдоним C# | Соответствие CLS | Системный тип | Диапазон | Пояснения |
|---|---|---|---|---|
void |
+ | Void |
Пусто | Применяется для соблюдения синтаксиса |
sbyte |
- | SByte |
-128 —127 | Знаковое 8 бит |
byte |
+ | Byte |
0 —255 | Беззнаковое 8 бит |
short |
+ | Int16 |
-32'768 —32'767 | Знаковое 16 бит |
ushort |
- | UInt16 |
0 —65'535 | Беззнаковое 16 бит |
int |
+ | Int32 |
-2'147'483'648 —2'147'483'647 | Знаковое 32 бит (4 байта) |
uint |
- | UInt32 |
0 —4'294'967'295 | Беззнаковое 32 бит |
long |
+ | Int64 |
-9'223'372'036'854'775'808 —9'223'372'036'854'775'807 | Знаковое 64 бит (8 байт) |
ulong |
- | UInt64 |
0 —18'446'774'073'709'551'615 | Беззнаковое 64 бит (8 байт) |
char |
+ | Char |
U+0000 —U+FFFF | Для описания только одного символа Unicode 16 бит |
float |
+ | Single |
1.5x10-45 —3.4x1038 | Знаковое с плавающей точкой 32 бит (4 байта) |
double |
+ | Double |
5.0x10-324— 1.7x10308 | Знаковое с плавающей точкой 64 бит (8 байт) |
bool |
+ | Boolean |
true или false |
Логическое |
decimal |
+ | Decimal |
100 —1028 | Знаковое 96 бит (12 байт) |
string |
+ | String |
Ограничено только системной памятью | Для описания строки символов Unicode любой длины |
object |
+ | Object |
Практически все что угодно. Все типы происходят от System.Object, поэтому объектом является все |
Класс, базовый для всех типов в .NET |
Полезное средство для исследования сборок - утилита ILDasm ( ILDasm - Intermediate Language Disassembler utility ), Ее вызов можно подключить к оболочке Visual Studio 2005 с помощью команды:
Tools/External Tools...

Утилита находится в
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe
После запуска утилиты в ее меню File/Open можно открыть нужную сборку, например, системную
C:\WINNT\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

Для просмотра метаданных типов достаточно загрузить сборку в ILDasm и нажать клавиши Ctrl-M. Команда меню Dump TreeView утилиты выгружает иерархию исследуемой сборки в текстовый файл. Графические значки будут заменены соответствующими текстовыми аббревиатурами.
Через панель Solution Explorer/References подключим сборку system.windows.forms.dll
Создайте новый проект 
Если открыть панель Object Browser, то можно увидеть подключенные библиотечные сборки, которые оболочка посчитала (по мнению ее разработчиков) необходимым присоединить к заготовке проекта

Удалим ссылки на сборки System.Data и System.Xml, которые нужны при работе с базами данных и языком XML. Через панель Object Browser это сделать нельзя (она только для просмотра), только в панели Solution Explorer.
В панели Solution Explorer раскройте узел References, выделите обе сборки System.Data и System.Xml, затем командой Remove контекстного меню удалите их из проекта
Командой Project/Add Reference откройте окно Add Reference и добавьте в проект ссылку на библиотечную сборку System.Windows.Forms
Каждая сборка содержит множество различных пространств имен, которые нужны для однозначной адресации к типам, имеющим возможно одинаковые имена. Добавленную сборку System.Windows.Forms мы будем использовать для создания графического окна Windows в нашем
Через панель Solution Explorer откройте файл-заготовку Program.cs и откорректируйте его следующим образом// Подключение пространств имен
// для сокращенной адресации классов
using System;
using System.Windows.Forms;
// Пространство имен в проекте можно и не объявлять
// Тогда будет использовано глобальное пространство имен
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консольного окна сборки mscorlib.dll
Console.Title = "Это консольное окно";
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WindowWidth = 50; // Ширина в знакоместах
Console.WindowHeight = 10; // Высота в текстовых строках
Console.CursorVisible = false;
Console.WriteLine("Простая C# - программа");
// Графическое окно сборки System.Windows.Forms.dll
MessageBox.Show("Привет из графического окна сообщений!!!");
Console.ReadLine();
}
}
}
Результат выполнения приложения будет таким
abstract |
as |
base |
bool |
break |
byte |
case |
catch |
char |
checked |
class |
const |
continue |
decimal |
default |
delegate |
do |
double |
else |
enum |
event |
explicit |
extern |
false |
finally |
fixed |
float |
for |
foreach |
goto |
if |
|
in |
int |
interface |
internal |
is |
lock |
long |
namespace |
new |
null |
object |
operator |
out |
override |
params |
private |
protected |
public |
readonly |
ref |
return |
sbyte |
sealed |
short |
sizeof |
stackalloc |
static |
string |
struct |
switch |
this |
throw |
true |
try |
typeof |
uint |
ulong |
|
|
ushort |
using |
virtual |
void |
|
while |
Разработка кода программы C# сводится к последовательному развитию типов, главным из которых является класс. Каждый класс рекомендуется размещать, как самостоятельный фрагмент кода программы, в отдельном файле с расширением *.cs. Но и в одном файле можно размещать сколько угодно классов, просто неудобно в них разбираться.
Внутри класса дается объявление-реализация методов, определяющих его функциональность. Каждый класс размещается внутри именованных скобок namespace Имя{...}, задающих простанство имен, хотя это и необязательно - тогда будет использовано глобальное пространство имен. Кроме того, вначале файла с пользовательским классом перечисляются пространства имен используемых библиотечных классов для сокращенного их вызова. Объявление класса не должно заканчиваться точкой_с_запятой, как это принято в C++.
Аргументами метода Main() являются Main()
static void Main(string[] args) {...}
|
|---|
public static void Main(string[] args) {...} |
public static void Main() {...} |
private static void Main(string[] args) {...} |
private static void Main() {...} |
static int Main(string[] args) {... return 0; } |
public static int Main(string[] args) {... return 0; } |
public static int Main() {... return 0; } |
private static int Main(string[] args) {... return 0; } |
private static int Main() {... return 0; } |
Главное требование - функция Main() должна быть статической, поскольку запускается тогда, когда еще не существует ни одного экземпляра класса. А объявлена она общедоступной или приватной - система ее всегда найдет.
Вернемся к медоту Main() нашего приложения, вернее, к его заголовку.
Скорректируйте файл Program.cs, чтобы он выглядел такusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Простая C# - программа");
Console.ReadLine();// Для задержки окна
}
}
}
Измените метод Main() так, чтобы внутри него перебирались using System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine("Arg: {0}", args[i]);
}
Console.ReadLine();// Для задержки окна
}
}
}
При запуске приложения никаких параметров не будет, поскольку мы их не задали.
Необходимо выполнить следующее:
Windows через меню Пуск/ ВыполнитьОбзор найти исполнимую сборку проектаНапример, командная строка
C:\Tmp\Test.exe -arg1 -arg2 "-Привет семье" -345
при запуске нашего последнего примера даст результат
Теперь то же самое выполним, используя окно командной строки оболочки. Это окно позволяет выполнить команды непосредственно в интегрированной среде разработки. Через него можно выполнить любую команду меню и даже те команды, которые не предусмотрены ни на каком меню.
Чтобы отобразить окно команд, выполните опцию меню View/Windows Other/Command Window
Введите в окне команд после промптера строку, состоящую из вызова процессора команд ( Shell ), полного пути нахождения нашей сборки, имени сборки и аргументов, разделенных символами пробела. У меня это будет выглядеть так
Если путь содержит составные имена папок, разделенные пробелами, то вызов нужно заключать в двойные кавычки, например

После завершения команды нажатием клавиши Enter запустится консольное окно со следующим результатом
Нажмите клавишу Enter, чтобы прервать работу Перебор введенных ранее команд в окне Command Window выполняется стрелками "вверх/вниз" клавиатуры .
Для задания аргументов в режиме Debug сделайте следующее:
В панели Solution Explorer щелкните на пиктограмме Properties
То же самое можно сделать, если выполнить команду меню Project/MyApp Properties или для узла проекта вызвать контекстное меню и выбрать опцию Properties

Настройте окно свойств проекта для вкладки Debug так
Выполните команду Debug/Start Debugging или щелкните на одноименной кнопке окна оболочкиПолучим результат
Замените в методе Main() цикл for на foreach, получив такой кодusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Точка входа может быть без аргументов
static void Main(string[] args)
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
foreach (string arg in args)
{
Console.WriteLine("Arg: {0}", arg);
}
Console.ReadLine();// Для задержки окна
}
}
}
Постройте приложение - результат будет прежнимИзвлечение параметров методом Environment.GetCommandLineArgs()
using System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Выводим параметры на консоль
Console.WriteLine("Печать аргументов:");
// Аргументы командной строки
// извлекаются классом Environment
string[] theArgs = Environment.GetCommandLineArgs();
Console.WriteLine("Путь к приложению: {0}", theArgs[0]);
for (int i = 1; i < theArgs.Length; i++)
Console.WriteLine("Аргумент: {0} ", theArgs[i]);
Console.ReadLine();// Для задержки окна
}
}
}
Постройте приложение - результат будет прежнимВсе объекты в C# создаются на куче и с помощью оператора new. Инициализация объектов при их создании выполняется функциями-членами класса, которые называются конструкторами. Конструкторы не нужно вызывать специально. Для статических классов существует только один статический конструктор, который не должен иметь аргументов. Они вызываются автоматически при создании объекта и только один раз. Повторно вызвать конструктор как метод для уже существующего объекта невозможно.
Для нестатических классов различают конструкторы по умолчанию и общие конструкторы. Конструктор по умолчанию не имеет аргументов. Программист может его определить сам или система предоставит его автоматически, если программистом не был объявлен ни один конструктор. Конструктор по умолчанию может быть у класса только один. Общие конструкторы имеют аргументы. Класс может иметь несколько общих конструкторов, только они должны различаться сигнатурой (количеством, типом и порядком следования аргументов), чтобы компилятор мог однозначно определить перегружаемый конструктор по сигнатуре в фактическом вызове.
Язык C# имеет существенное отличие от C++ в отношении конструктора по умолчанию. В C++ если программист не предусмотрел свой конструктор по умолчанию, то предоставляемый системой конструктор ничего с данными-членами класса не делает и они остаются неинициализированными. В C# конструктор по умолчанию, предоставляемый системой, производит инициализацию данных, большей частью нулем. Но если программист определил хоть один конструктор с параметрами, то конструктор по умолчанию системой уже не предоставляется и если он используется, то нужно определить свой конструктор по умолчанию, хоть с пустым телом.
Имена конструкторов должны совпадать с именем класса. Конструкторы должны имет
Для иллюстрации сказанного продолжим модификацию нашего проекта MyClass.
Модифицируйте класс следующим образомusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Внутренние переменные - поля
private int x, y;
// Конструктор по умолчанию
public MyClass()
{
Console.WriteLine("Конструктор по умолчанию");
}
// Конструктор с параметрами
public MyClass(int a, int b)
{
Console.WriteLine("Конструктор с параметрами");
x = a;
y = b;
}
// Сервисы класса
public int GetX() { return x; }
public int GetY() { return y; }
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем два объекта-экземпляра класса
MyClass ob1 = new MyClass(); // Вызов конструктора по умолчанию
Console.WriteLine("ob1: x = {0}; y = {1} \n", ob1.GetX(), ob1.GetY());
MyClass ob2 = new MyClass(10, 15); // Вызов параметризованного конструктора
Console.WriteLine("ob2: x = {0}; y = {1}", ob2.GetX(), ob2.GetY());
Console.Read();
}
}
}
Постройте проект, чтобы получить результат
Мы видим, что поля x и y в первом объекте не остались брошенными, как это бы было в C++, а инициализировались нулями в соответствии с их типом. Такой порядок инициализации по умолчанию смысловым типом не относится к неинициализированным ссылкам. Их нужно обязательно инициализировать нулевым адресом с помощью ключевого слова null.
Деструкторы в C# применять не нужно, хотя они и предусмотрены синтаксисом. Программист не должен заботиться об освобождении затребованной у системы памяти на куче. Об этом позаботиться специальный механизм, который называется сборщиком мусора ( GC - Garbage Collector ). Сборщик мусора платформы .NET освободит память автоматически.
В последнем примере мы внутри класса MyClass в методе Main() создавали объекты того же самого класса. Это вносит лишнюю путаницу в изучение кода. Функция Main() никакого отношения к классу MyClass не имеет и присутствует там только из-за того, что глобальные функции в C# недопустимы и ее нужно куда-то приткнуть. Вынесем ее в отдельный класс, чтобы отделить "котлеты от мух".
Вначале заменим сервисные методы доступа к закрытым полям класса на общедоступные свойства.
Измените код файла MyClass.cs так, как показано нижеusing System;
using System.Windows.Forms;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass(10, 15);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
Операторы get и set называются аксессорами, а ключевое слово value способно иметь значение любого типа. Свойство можно объявить "только для чтения", используя ключевое слово readonly, а можно просто убрать блок аксессора set.
Запустите приложение, чтобы получить результат
Теперь обращение к внутренним полям класса осуществляется через общедоступные свойства.
Упакуем функцию Main() в отдельный класс с именем EntryPoint, для этого
Выделите код функции Main() и выполните для выделенного фрагмента опцию контекстного меню Surround With (охватить с помощью)
В появившемся всплывающем меню выберите тип охватывающего блока classОболочка создаст MyClass. public, т.е. по нему можно создавать объекты только внутри внешнего класса. Кроме того, синтаксис C# не допускает в одном пространстве имен существование типов с одинаковыми именами.
Переименуйте новый класс в EntryPoint и запустите приложениеМы видим, что функция Main() доступна и из внутреннего класса.
Вынесите класс EntryPoint в пространство имен проекта и запустите приложение. Все остается прежним!
Для структурирования отступов в коде уничтожте последнюю закрывающую фигурную скобку пространства имен и вновь ее создайте. Код будет выровнен!Мы в данном примере нехорошо поступили (ну, почти нехорошо), потому что принято каждый класс размещать в отдельном файле с названием, обычно совпадающем с именем класса.
Разработчики оболочки предполагали, что в файле Program.cs будет находиться функция Main(), а пользовательский код желательно (но не обязательно) размещать в других файлах. Согласимся с ними.
Выберите опцию главного меню Project/Add New Item и заполните диалоговое окно мастера так
Перенесите в новый файл содержимое класса MyClassПосле этого коды файлов будут такими
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass(10, 15);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
using System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
В C++ в классе можно простые переменные только объявлять, а инициализировать их нужно в конструкторе или устанавливать путем присваивания функциями Set() из клиента. В C# такое ограничение отсутствует и данные-члены класса можно инициализировать при их объявлении в классе. Примеры
Измените код класса MyClass нашего примера так, как показано нижеusing System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x = 10, y = 15;
// Конструктор по умолчанию
public MyClass() { }
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем экземпляр класса
MyClass ob = new MyClass();
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
// Создаем еще один экземпляр,
// используя ту же ссылочную переменную
// Прежний объект будет утерян
ob = new MyClass(100, 150);
Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
Console.ReadLine();
}
}
}
Обратите внимание, что при создании объекта класса MyClass используется одна и та же ссылочная переменная, после присвоения которой нового значения прежний брошенный объект становится добычей сборщика мусора.
После построения проекта получим результат

Отметим, что подобная прямая инициализация членов-данных в структурах недопустима. Перегрузка конструктора по умолчанию в структурах тоже недопустима. Чтобы убедиться в этом, объявите MyClass как структуру.
В C# различают два вида типов: значимые ( value-based ) и ссылочные ( reference-based ). Переменная значимого типа представляет закрепленное за ней само значение оперативной памяти. Переменная ссылочного типа содержит лишь адрес области оперативной памяти с размещаемым в этой области значением. Все структурные переменные размещаются на стеке, а ссылочные - на управляемой куче ( managed heap ). К структурным типам относятся перечисления и структуры, а к ссылочным - классы, делегаты, интерфейсы.
Такое разделение принято для экономии памяти и быстродействия.
Когда выполняется присваивание одной значимой переменной другой, то создается побитовая копия данных, которые размещены в разных областях стековой памяти и существуют независимо друг от друга. При выполнении присваивания ссылочных переменных создается копия адреса, после чего обе переменные будут ссылаться на одну и ту же область памяти на управляемой куче. То есть создается как-бы псевдонимы одной и той же области памяти. Это означает, что изменения объекта по одной ссылочной переменной равноценно изменениям по псевдониму
Добавьте к нашему проекту файл с именем MyStruct.c s типа C #, выполнив команду Project/Add Clas s, и скопируйте в него код из файла MyClass.cs. Исправьте программу, чтобы она была такойusing System;
namespace MyApp
{
struct MyStruct
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyStruct(int x, int y)
{
this.x = x;
this.y = y;
}
// Конструктор по умолчанию перегружать нельзя
// public MyStruct() { }
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class MyClass
{
// Внутренние (по умолчанию) переменные - поля
int x, y;
// Конструктор с параметрами
public MyClass(int x, int y)
{
this.x = x;
this.y = y;
}
// Конструктор по умолчанию
public MyClass() { }
// Общедоступные свойства класса
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
}
using System;
namespace MyApp
{
class EntryPoint
{
// Точка входа в программу без аргументов
static void Main()
{
// Настройка консоли
Console.Title = "";
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
// Создаем две структуры
MyStruct struct1 = new MyStruct(10, 15);
MyStruct struct2 = new MyStruct();
// Создаем два объекта
MyClass object1 = new MyClass(10, 15);
MyClass object2 = new MyClass();
// Печатаем
Console.WriteLine("Структуры до изменений:");
Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
Console.WriteLine("\nОбъекты до изменений:");
Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);
// Присваиваем значения
struct1 = struct2;// Копируются значения, struct1 существует
object1 = object2; // Копируется адрес, object1 теряется
// Изменяем экземпляры
struct2.X = 20; struct2.Y = 30;
object2.X = 20; object2.Y = 30;
// Печатаем
Console.WriteLine("\nСтруктуры после изменений:");
Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
Console.WriteLine("\nОбъект после изменений:");
Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);
Console.ReadLine();
}
}
}
Результат выполнения будет таким

При копировании переменных-структур копируются значения прикрепленных структур, а при копировании переменных-объектов копируются значения адресов. Структуры остаются адресуемыми по своим переменным, только приобретают равные значения данных. При смене значения ссылки на объект этот объект теряется для программы, а обе ссылки начинают адресовать один и тот же объект в памяти, созданный динамически на управляемой куче.
Поставим некоторые предварительные вопросы и ответы на них в таблице
| Вопрос | Значимые типы | Ссылочные типы |
|---|---|---|
| Место размещения | Стек | Управляемая куча |
| Как будет представлена переменная, которой в качестве значения присвоен этот тип | В виде локальной побитовой копии типа | В виде указателя на область оперативной памяти, относящейся к объекту этого типа |
| Что может являться для них базовым типом | Эти типы могут производиться только напрямую от типа System.ValueType |
Могут производиться от любого другого типа, кроме System.ValueType, если этот тип не является закрытым |
| Может ли этот тип выступать производителем другого типа | Нет. |
Да, если этот ссылочный тип не определен как закрытый |
| Как производится передача параметров этого типа в функцию | Только по значению, т.е. вызывающей функции передаются локальные копии значений переменных | Передаются как ссылки, т.е. вызывающей функции передаются адреса размещения соответствующих объектов в оперативной памяти |
Существует ли возможность переопределить встроенный метод Object.Finalize() |
Нет. |
Да, но не напрямую |
| Можно ли определить конструкторы для этого типа | Да, но конструктор по умолчанию зарезервирован, поэтому все определяемые конструкторы должны иметь параметры | Да, сколько угодно, лишь бы они различались сигнатурой |
| Когда происходит разрушение переменной данного типа | Когда происходит выход из области видимости | Во время процесса сборки мусора в управляемой куче с помощью механизма (сборщик мусора) |
| Можно ли напрямую инициализировать данные при объявлении типа | Нет | Да |
| Можно ли перегружать конструктор по умолчанию | Нет, он зарезервирован за системой | Да, и обязательно, если определен хоть один конструктор с параметрами |
Несмотря на все различия, значимые и ссылочные типы могут иметь и поддерживать как встроенные (стандартные), так и определенные пользователем (программистом) методы, свойства, события в необходимом количестве. Но удобно за значимыми типами закреплять только поддержку сложных данных.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.