C# — [C#]Указание на переменные без конкретного имени


Переменные и константы в C#

Важнейшим свойством алгоритма (а значит и программы), как известно, является массовость (обработка различных данных). Поэтому в программировании появились переменные, предназначенные для размещения данных. Любая переменная должна объявлена (присвоено имя — идентификатор) и инциализирована (задано какое-либо значение, принадлежащее к определенному типу). Допускается динамическая инициализация переменных (в процессе выполнения кода программы). Возможно использование неявно типизированных переменных. Поясняется целесообразность объявления констант.

Рассмотрим эти понятия на примерах.

Идентификатор — это имя сущности в коде. Существует стандарт именования идентификаторов, который следует использовать в коде.
Идентификатор может :
— начинаться с символа «_»;
— содержать заглавные и строчные буквы в формате Unicode;
— регистр имеет значение.
Идентификатор не может :
— начинаться с цифры;
— начинаться с символа, если это ключевое слово;
— содержать более 511 символов.
По соглашению (не обязательно, но желательно):
1. Параметры, локальные переменные и частные (private) свойства и методы пишутся в camel case (слова пишутся слитно, без пробелов и нижних подчеркиваний, каждое новое слово кроме первого с заглавной буквы, например, myVariavle).
2. Все остальные идентификаторы — в стиле Pascal case (тоже самое, что и camel case, только первое слово с заглавной буквы, например, MyClass).
Мое примечание к подсказке. Вы можете использовать кроме латинских (английских) букв и буквы русского алфавита (как, впрочем и других национальных алфавитов), однако не увлекайтесь этим. Более привычно, когда в идентификаторе используются буквы английского алфавита, цифры и символ подчеркивания (прежний стандарт).

Объявления переменных

Шаблон объявления переменной в C# выглядит следующим образом:
ТипДанных Идентификатор;
Например:
int k1;
System.Int32 _counter;
Int32 счетчик;

Все три переменных: k1, _counter, счетчик являются переменными одного типа, занимают в памяти 4 байта.

Заметим, что использование символов Юникода в написании идентификаторов разрешает использование букв национальных алфавитов, включая русский.

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

Кроме того, тип переменной нельзя изменять в течение срока ее существования. В частности, переменную типа int нельзя преобразовать в переменную типа char.

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

Инициализация переменной

Задать значение переменной можно, в частности, с помощью оператора присваивания. Кроме того, задать начальное значение переменной можно при ее объявлении. Для этого после имени переменной указывается знак равенства (=) и присваиваемое значение. Если две или более переменные одного и того же типа объявляются списком, разделяемым запятыми, то этим переменным можно задать, например, начальное значение.
Ниже приведена общая форма инициализации переменной:
int k1 = 10;
char символ = ‘Z’;
float f = 15.7F;
int x = 5, y = 10, z = 12;

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

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

Например, в C# поступить следующим образом нельзя:
public static int принт()
<
int d;
Console.WriteLine(d); return 0;
>

Получим при компиляции сообщение об ошибке: Использование локальной переменной “d”, которой не присвоено значение.

Динамическая инициализация

В приведенных выше примерах в качестве инициализаторов переменных использовались только константы, но в C# допускается также динамическая инициализация переменных с помощью любого выражения, действительного на момент объявления переменной:
int a = 3, b = 4;
// Инициализируем динамически переменную c:
double c = Math.Sqrt(a * a + b * b);
Console.WriteLine(«<0>», c);

В данном примере объявляются три локальные переменные a,b,c, первые две из которых инициализируются константами, а переменная c инициализируется динамически с использованием метода Math.Sqrt(), возвращающего квадратный корень выражения.

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

Неявно типизированные переменные

Теперь некоторое отступление от строгих правил, привыкайте и к исключениям (по англ. — Exception). Как пояснялось выше, все переменные в C# должны быть объявлены. Как правило, при объявлении переменной сначала указывается тип, например int или bool, а затем имя переменной. Но начиная с версии C# 3.0, компилятору предоставляется возможность самому определить тип локальной переменной, исходя из значения, которым она инициализируется.
Такая переменная называется неявно типизированной. Неявно типизированная переменная объявляется с помощью ключевого слова var и должна быть непременно инициализирована.
Для определения типа этой переменной компилятору служит тип ее инициализатора, то есть значения, которым она инициализируется:
var n = 12; // переменная i инициализируется целочисленным литералом
var d = 12.3; // переменная d инициализируется литералом
// с плавающей точкой, имеющему тип double
var f = 0.34F; // переменная f теперь имеет тип float

Единственное отличие неявно типизированной переменной от обычной, явно типизированной переменной, — в способе определения ее типа. Как только этот тип будет определен, он закрепляется за переменной до конца ее существования.
Неявно типизированные переменные внедрены в C# не для того, чтобы заменить собой обычные объявления переменных. Напротив, неявно типизированные переменные предназначены для особых случаев, и самый примечательный из них имеет отношение к языку интегрированных запросов (LINQ).

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

Константы

Как следует из названия, константа — это переменная, значение которой не меняется за время ее существования. Предваряя переменную ключевым словом const при ее объявлении и инициализации, вы объявляете ее как константу:
const int N_max =100;
Идентификатор константы записывается по общим правилам написания идентификаторов (см. подсказку выше).
Ниже перечислены основные характеристики констант:
1. Они должны инициализироваться при объявлении, и однажды присвоенные им значения никогда не могут быть изменены.
2. Константа не может быть объявлена непосредственно в пространстве имен, но может быть объявлена либо в классе, либо в функции.
3. Значение константы должно быть вычислено во время компиляции.

Таким образом, инициализировать константу значением, взятым из другой переменной, нельзя. Если все-таки нужно это сделать, используйте поля только для чтения.
Константы всегда неявно статические. Нет необходимости включать модификатор static в объявление константы.
Использование констант в программах обеспечивает, по крайней мере, три преимущества:
1. Константы облегчают чтение программ, заменяя «магические» числа и строки читаемыми именами, назначение которых легко понять. Например, через константу N_max может задать максимальное количество элементов в массиве объектов, при необходимости это число может быть изменено всего лишь в одном операторе объявления константы.
2. Константы облегчают модификацию программ. Например, предположим, что в программе C# имеется константа IncomeTax (подоходный налог), которой присвоено значение 13 процентов. Если налог когда-нибудь изменится, вы можете модифицировать все вычисления налога, просто присвоив новое значение этой константе, и не понадобится просматривать код в поисках значений и изменять каждое из них, надеясь, что оно нигде не будет пропущено.
3. Константы позволяют избежать ошибок в программах. Если попытаться присвоить новое значение константе где-то в другом месте программы, а не там, где она объявлена, компилятор выдаст сообщение об ошибке.

В наших примерах константы используются достаточно часто.

Следующая небольшая тема раздела: Область видимости переменных


Урок по C# №4:Переменные и типы данных. Часть 2

В прошлом уроке вы познакомились с переменными и поняли, что в программировании без них никак. C# -строго типизированный язык, а это значит, что мало объявить переменную –нужно соотнести ее с подходящим типом. А вот теперь обратите внимание – в C# типы данных разделяются на две отличных друг от друга группы. Одна группа включает в себя так называемые типы-значения (value types). Фактически, это обычные примитивные типы данных, которые есть в любом сиподобном языке. Ниже я приведу список этих типов с кратким описанием. Я не буду загружать вас дипазонами значений этих типов – всегда можно посмотреть это, написав простенькую программку для этого. Итак, вот основные базовые типы языка C#:

  • char – символьный тип. Предназначен для хранения любых символов в вашей программе.
  • bool— логический тип. Может принимать либо true, либо fasle. Переменная, объявленная этим типом будет иметь по-умолчанию true
  • byte – байт он, как говорится, и в Африке байт. Это означает, что в него может влезть всего число 0 до 255. Почему 255? Все просто – один байт равен 8 битам. А 2 в степени 8 равно 256. Но у нас есть 0, поэтому предельное число будет 255. Этот тип применяется тогда, когда нужно хранить небольшое значение, например, возраст. Учтите, что если вздумаете присвоить переменной этого типа значение больше 255, то компилятор выдаст ошибку. Кроме того, тип byte является беззнаковым – то есть, он может хранить только положительные числа.
  • sbyte – это тот же byte, но только переменная этого типа может иметь отрицательное значение. В этом случае диапазон значений будет лежать от -128 до 127
  • int – это тип данных, котором можно хранить только целые числа. Это самый основной тип для манипуляций с целыми числами. Обычно его хватает для большинства задач. Однако изредка может понадобиться хранить очень большое значение или наоборот, маленькое (хотя для маленьких чисел можно также смело ставить тип int). Поэтому тип int разделяется на свои подвиды:
  • short – короткое целое число.
  • ushort – тоже самое, только без знака.
  • long –длинное целое, предназначенное для хранения больших чисел. Больше него только тип decimal. Запомните важную вещь – компилятору нужно указывать принадлежность переменной к данному типу суффиксомL илиl. Например, long a=78485994L . Иначе компилятор посчитает переменную типом int.
  • ulong – только положительные числа long
  • uint –только положительные числа int
  • decimal – хранит ну очень большие целые числа. Самое оно для финансовых программ!
  • float – содержит дробные числа (так называемые вещественные числа или числа с плавающей точкой) . Подобно типу long требует указание суффикса f .Например, float floatvalue = 34.6f
  • double – вещественные числа с двойной точностью после знака запятой. По-умолчанию, это основной тип для всех дробных чисел.
  • string – строка. Тут, я думаю, все понятно.

Кроме того, к значимым типам относятся также следующие типы, определяемые пользователем. Это:

  • enum – перечисления. О них поговорим в других уроках.
  • struct – структуры. Изучим их значительно позже.

В противовес значимым типам в C# была введена вторая группа типов – это ссылочные типы. К ним относятся классы, о которых мы поговорим значительно позже. Почему были введены такие группы? Если думаете, что все это для того, чтобы запутать программиста, то вы ошибаетесь. Дело в том, что тип данных напрямую влияет на производительность вашего кода. Причина этого явления заключается в том, что переменные этих типов данных размещаются в разных областях памяти — в так называемой куче (это вся ваша оперативная память) лежат классы, а в быстрой стековой памяти лежат небольшие по своим размерам типы-значения. Это очень удачных подход, так как работа со стеком очень быстрая. Однако стек имеет очень подлое свойство – он ограничен в своих размерах. Поэтому переменные типов-значений занимают мало памяти иначе просто произойдет переполнение стека, а это не очень приятная вещь. Чтобы этого не случилось, для переменных, занимающих много памяти (это как раз объекты ваших классов) выделяется место в куче. Единственный неприятный момент подобного подхода – переменную гораздо быстрее достать из стека, нежели из кучи. Поэтому в C# был принят подход, в котором в стеке хранится ссылка на область кучи. Это ускоряет работу. Итак, вы уже знаете основные типы данных. Теперь, давайте рассмотрим как объявляются переменные. Синтаксис предельно прост: тип_переменной имя_переменной ; Учтите, что можно объявить сразу несколько переменных одного типа в одной декларации:

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

C# поддерживает Unicode, а это означает, что можно объявлять имена переменных русскими буквами:

Но так делать обычно не рекомендуется, хотя сейчас я часто вижу такой подход. Об этом подходе мы поговорим ниже.

Как видите, все просто. А теперь давайте рассмотрим случай, который все больше набирает обороты в российских айти-компаниях.

Помните оператор using? С помощью него мы подключали пространства имен. Однако это не все, что он умеет. Дело в том, что с помощью using можно создавать псевдонимы всех типов. Давайте в качестве примера создадим проект, в котором мы сделаем русские аналоги встроенных типов.

Создайте консольный проект в Visual Studio или Sharp Developer. Назовите его lesson 2 и добавьте в код следующие строки:

Теперь в коде можно использовать наши псевдонимы вместо англоязычных определений. Посмотрите, например, такой код:

Занятно выглядит, не так ли? Фактически, можно так «русифицировать» буквально все. Но я не приветствую данный подход. В любом случае, поэкспериментируйте. А пока на этом урок окончен.

Переменные в C# (Csharp)

С# — Научиться или умереть

1b) Переменные
Ну что ж, знать WriteLine() — очень хорошо, но если мы хотим сделать что-нибудь дельное, нам надо познакомиться с переменными. Из-за того, что они очень важны в работе любой программы, нам придется изрядно потрудиться. Но не надо вешать нос! То что мы выучим сегодня точно также работает и в Java, и в C++. Так что мы изучим такое, что приготся и в других языках!

Переменная работает как хранилище, сосуд для информации. Зачем нам это надо? В общем, для гибкости. Мы можем вложить в нее любое значение, а потом использовать и изменять как душе угодно, да и когда душе угодно. Зачем писать код десятки раз, когда можно сделать это однажды и просто использовать одну переменную с нужными нам данными? Переменные особенно важны когда имеем дело с вводом данных извне и понадобится где-то хранить информацию, которую предоставил пользователь. Прежде чем войти в эту быструю реку, познакомимся с переменными поближе, узнаем как они работают. Смотрим: Мдааа. Достаточно загадочно , нет? Как мы наверно уже догадались «string» («строка») — это просто термин, который подразумевает текст («строка» букв,если угодно). Код свыше должен выдать такой результат: Теперь, когда мы знаем с чем имеем дело, давайте внимательнее посмотрим на строку, где мы «объявили» переменную (кстати, говорю это впервые). Первое слово в строке, «string», говорит С# что мы создаем переменную строкового типа (да, есть переменные и другого, но это позже, а сейчас СКОНЦЕНТРИРУЙСЯ!) Второе слово, «programmerName»(» имяПрограммиста» в переводе) — имя нашей переменной. Имена могут быть разнообразной последовательностью букв,чисел и подчеркиваний. Если имя не начинается с цифры, или содержит знаки пунктуации кроме подчеркивания, это правильные имена. Очевидно, что необходимо сделать имя переменой как можно более полезной, например своим именем указать на содержание. То есть в данном случае «programmerName» — отличное имя для переменной, а «engineOil»(«Машинное масло» в переводе) — нет.

Как задать имя экземпляра класса C# из переменной?

Добрый день,
как бы Вы решили задачу установки имени переменной, которая ссылается на экземпляра класса «Футбольная команда» в соответствии с Именем команды введенным пользователем в командной строке?

То есть в процессе выполнения IL кода создается экземляр класса:
FootballTeam Team1 = new FootballTeam();
Как вместо Team1 указывать имя ссылки полученное от пользователя?
Например пользователь указал название новой команды «Manchester», я хочу чтобы ссылка на экземпляр класса также имела название «Manchester» а не Team1. Как это сделать?

Ниже код просто для описания входящих условий задачи, это — не решение.


  • Вопрос задан более трёх лет назад
  • 1637 просмотров

Ну вообще я так понимаю, что здесь требуется сгенерировать IL-код в рантайме. А ваше решение вообще не отражает суть задачи. Вы поменяли свойство экземпляра, а никак не имя экземпляра. С другой стороны вопрос так поставлен, что сложно понять что на деле хотят, что такое имя экземпляра класса. Это для каждой команды своё имя класса, чтобы через рефлексию можно было его получить или это имя переменной хранящей ссылку на экземпляр класса команды.

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

C# — [C#]Указание на переменные без конкретного имени

По-моему, называть свойство, так же как и тип, не очень правильно. Можно начать путаться.

Нет, это нормально. Наоборот, выдумывать странные имена для полей и классов, чтобы они не совпадали, было бы неправильно

Я всего лишь вот о чем:

Опять же, почему-то с трудом представляется класс в которому, допустим будет такое:

А ведь компилируется.

В случае Forum.Threads Thread.Forum все таки название типа и поля разные. Хотя бы числом.

Опять же, почему-то с трудом представляется класс в которому, допустим будет такое:

А ведь компилируется.

В случае Forum.Threads Thread.Forum все таки название типа и поля разные. Хотя бы числом.

На самом деле просто лочигно, что у класса SomeClass есть описание, оно и называется Description. То есть это понятное название.

А вот название для переменной Int32 — совсем не очевидное. Скорее всего, оно что-то означает (например, количество чего-то, или номер и т.д.) и называться должно соответствующий образом: Number, Amount и т.д.

А на сколько логично пытаться определить где тут используется экземпляр, а где статическое поле класса Description?
Без this читабельность кода падает, как ни крути.

По поводу логичности: для кого-то ведь будет вполне логично, что у класса SomeClass есть описание, и то что оно не должно называться описание, потому что тип итак указывает, на то что оно описание. (Масло масляное, кошка по имени Кошка и т.д.)

Ладно, начинаем уже флеймить. Предлагаю тему закрывать. Вышеизложенной информации, особенно в начале обсуждения, вполне должно хватить топикстартеру.

А на сколько логично пытаться определить где тут используется экземпляр, а где статическое поле класса Description?
Без this читабельность кода падает, как ни крути.

По поводу логичности: для кого-то ведь будет вполне логично, что у класса SomeClass есть описание, и то что оно не должно называться описание, потому что тип итак указывает, на то что оно описание. (Масло масляное, кошка по имени Кошка и т.д.)

Ладно, начинаем уже флеймить. Предлагаю тему закрывать. Вышеизложенной информации, особенно в начале обсуждения, вполне должно хватить топикстартеру.

Так дело ж именно в том что именно кошка и осталась кошкой. Мы пописали кошку как понятие (class). Потом создали класс млекопитающие одним из вложенных классов которого и есть кошка (не важно как представлены открытыми членами или полями).

Если же я бы описывал создание объекта на основе класса могла бы звучать и МояКошка.


Вот в чем суть по этому в C++ и придумывали стандарт что-то типа m_Кошка, хотя описывали тоже абстрактное вложенное понятие, а не конкретный экземпляр. Вот я и подумал что в MS ввели какую-нибудь стандартизацию. А-то честно говоря придумывать разные названия описывающие одно и тоже понятие, а потом еще и не забыть их.

Переменные C#

Переменные и типы — тесно связанные понятия. С объектной точки зрения переменная — это экземпляр типа. Скалярную переменную можно рассматривать как сущность, обладающую именем, значением и типом. Имя и тип задаются при объявлении переменной и остаются неизменными на все время ее жизни. Значение переменной может меняться в ходе вычислений, эта возможность вариации значений и дало имя понятию переменная (Variable) в математике и программировании. Получение начального значения переменной называется ее инициализацией. Важной новинкой языка C# является требование обязательной инициализации переменной до начала ее использования. Попытка использовать неинициализированную переменную приводит к ошибкам, обнаруживаемым еще на этапе компиляции. Инициализация переменных, как правило, выполняется в момент объявления, хотя и может быть отложена.

Общий синтаксис объявления сущностей в C# похож на синтаксис объявления в C++, хотя и имеет ряд отличий. Вот какова общая структура объявления:

При объявлении переменных чаще всего задаются модификаторы доступа — public, private и другие. Если атрибуты и модификаторы могут и не указываться в объявлении, то задание типа необходимо всегда.

При объявлении простых переменных указывается их тип и список объявителей, где объявитель — это имя или имя с инициализацией. Список объявителей позволяет в одном объявлении задать несколько переменных одного типа. Если объявитель задается именем переменной, то имеет место объявление с отложенной инициализацией. Хороший стиль программирования предполагает задание инициализации переменной в момент ее объявления. Инициализацию можно осуществлять двояко — обычным присваиванием или в объектной манере. Во втором случае для переменной используется конструкция new и вызывается конструктор по умолчанию. Процедура SimpleVars иллюстрирует различные способы объявления переменных и простейшие вычисления над ними:

public void SimpleVars()

//Объявления локальных переменных

int x, s; //без инициализации

int y =0, u = 77; //обычный способ инициализации

float w1=0f, w2 = 5.5f, w3 =w1+ w2 + 125.25f;

//допустимая инициализация в объектном стиле

Получить имя класса из переменной объекта

Я хотел бы получить класс, на который указана переменная Object.

Например, если у меня есть экземпляр объекта StringBuilder, который я впоследствии установил для переменной Object, могу ли я как-то узнать, что переменная Object указывает на объект StringBuilder?

Я попытался использовать typeof(oMyObject) или oMyObject.GetType() в каждой комбинации, о которой я могу думать, и все еще не придумываю ничего, кроме Object . Похоже, должен быть довольно прямой способ сделать это, и, вероятно, есть, но я не нахожу его.

Я должен не соглашаться с пользователем, который отметил это как дублированный вопрос к тому, к которому они предоставили ссылку. Название моего вопроса может быть не таким ясным, как могло бы быть (я немного изменил его сейчас), и ответы на оба могут включать один и тот же метод, но пользователь, который задал другой вопрос, искал способ создания экземпляра объект того же типа, что и другой объект. Я искал способ получить имя класса, когда все, что у меня есть, является переменной типа Object. Я бы никогда не придумал ответ, который представил Рид, рассматривая этот вопрос, и я не помню, чтобы он когда-либо появлялся в поиске на этом сайте или более широком поиске Google.

GetType() должен обеспечить правильный System.Type объекта во время выполнения.

Например, это печатает «StringBuilder» :

Обратите внимание, что если вы просто хотите проверить для конкретного типа класса, is (или, as ) часто работает более чисто, чем получение Type :

Возможно, потому, что вы храните Type в объект. Попробуй это:

Как определить, что oMyObject указывает на экземпляр объекта StringBuilder, используя только oMyObject

вы можете сделать это

Однако, если вам нужно получить тип, вам нужно использовать GetType для объекта.

Все они будут работать:


Посмотрите другие вопросы по меткам c# c#-4.0 или Задайте вопрос

Типы данных и создание переменной на языке C# (sharp)

Типы данных и создание переменной на языке C# (sharp)

«Типы данных и создание переменной на языке C# (sharp)» — давайте разберёмся!

Переменная — это область памяти с которой работает разработчик, присваивая ей определённые значения.

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

Присвоить значение — это значит что-то положить, поместить в переменную.

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

Стандартные типы данных

Стандартных типов много, не правда ли? А специальных типов ещё больше…

Заучивать данную таблицу не имеет смысла (в ходе практики Вы их вызубрите как собственное имя из-за частоты использования), но вот иметь под рукой данную табличку смысл имеет. Сохраните данную страницу себе в закладки и всегда будете иметь под рукой небольшой справочник!

Как не сложно заметить, напротив каждого типа указан его вес в битах (единица измерения информации). Если Вы создали переменную определённого типа, то она занимает указанное количество памяти в компьютере. Каждый тип имеет свой диапазон допустимых значений, то есть какое максимально/минимально возможное значение можно положить в переменную с данным типом.
Все типы у которых в диапазоне имеются дробные значения (например, 5.0 или 7.9) поддерживают присвоение дробного (не целого) значения в переменную. Такие типы ещё называются «тип с плавающей точкой«.

Правда о типах или как стать Великим

При написании программ будьте бдительны, выбирайте такой тип, который потребует под себя как можно меньше памяти. В средних проектах переменных могут быть миллионы.
Давайте подумаем: зачем экономить? Компьютер мощный, памяти много, выберу самый объёмный тип!
Возьмём тип decimal (128 бит -> 16 байт) и умножим это число на один миллион (количество переменных), получаем 16*1000000 = 16000000 байт -> 15.26 мегабайт. Вот столько займут только Ваши переменные, а ведь ещё нужно пространство на различные процессы и действия самого компилятора. Мягко выражаясь, программа начинает кушать очень много памяти и в следствии чего имеет больший шанс «тормозить» в ходе своей работы. А если можно было обойтись типом int (32 бит -> 4 байта) во всей программе, то получим 4*1000000 = 4000000 байт -> 3.81 мегабайт. 15.26 против 3.81 — ощутимая разница, не правда ли? Ещё приложите сюда занимаемую память другими программами на Вашем компьютере и такие цифры получаются…

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

Создание переменной на C# (sharp)

В языке C# очень большую роль играет то, как Вы назовёте переменную. На само значение имени компилятору по большому барабану, но вот на регистр букв (заглавная или строчная) он смотрит особо внимательно. То есть, переменные с именами «Var1» и «var1» для компилятора абсолютно разные, и это из-за того, что первая буква в первом случае заглавная, а во втором прописная.

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

Функции для ввода и вывода в языке C#

Функции для консольного ввода и вывода в языке C# существенно отличаются от аналогичных функций языка С++. Таких функций 4, и все они являются членами класса Console . Функции WriteLine и Write обеспечивают вывод на экран и отличаются тем, что функция WriteLine после вывода обеспечивает перевод вывода на новую строку на экране, а функция Write такой перевод не выполняет. Для ввода применяются функции ReadLine и ReadKey . Функция ReadLine обеспечивает ввод строки. Если содержимое строки является числом, то требуется преобразование строки в число. Такое преобразование выполняет функция Parse , которая записывается после точки после имени типа данных. Для обратного перевода в строку текста применяется функция ToString . Функция ReadKey позволяет ввести код нажатой клавиши, и она часто применяется для останова экрана пользователя после вывода на экран. Если эту функцию не записать, то экран быстро исчезнет, не дав возможности посмотреть, что было выведено. Ниже приведен пример.

Console .WriteLine( «Введите число» );

// С переходом на новую строку

a = double .Parse( Console .ReadLine());

// Ввод с преобразованием

Console .WriteLine( «Введите второе число» );


b = int .Parse( Console .ReadLine());

// Ввод с преобразованием

Console .WriteLine( «Сумма <0>и <1>= <2>» , a, b, c);

В кавычках в круглых скобках записывается поясняющий текст и задается формат вывода. Для указания формата применяются пары фигурных скобок, в которых записываются целые числа, указывающие на порядок вывода переменных, имена которых записаны после кавычек. Отсчет начинается от нуля.

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

В списке переменных для вывода можно применять арифметические выражения. Например, строку вывода из примера выше можно записать и так (в примере выше без использования переменной c ):

Console .WriteLine( «Сумма <0>и <1>= <2>» , a, b, a + b);

Если требуется ввести числа и любые другие элементы в одной строке, в том числе и разнотипные (тип данных var будет описан ниже):

Лекции по языку C# v 2.3

var str = Console .ReadLine();

var result = str.Split( new [ ] < ' ' , ';' >); foreach ( var a in result)

Console .Write( » <0>» , a);

Метод Split позволяет разбить введенную строку на отдельные элементы. Конструкция new [ ] указывает, что будет сформирован массив, количество элементов которого заранее неизвестно. В фигурных скобках в апострофах указывается символ, являющийся разграничителем между отдельными элементами во вводимой строке. Этот разграничитель может быть не один. В примере в качестве разграничителей заданы пробел и точка с запятой. Такая запись позволяет использовать любой из указанных разграничителей в любой последовательности, как отдельно, так и совместно. Переменная str является неявно типизированной. Тип данных var позволяет объявлять переменные, которые могут быть любого типа. Но особый интерес представляют таблицы, которые состоят из массивов строк. В этом случае переменной типа var является объект класса, которым является отдельная строка, а собственно таблица является массивом строк.

Управление форматом числовых данных:

При отображении данных на экране или при печати может потребоваться явное указание формы (формата) вывода, например, указание занимаемых позиций для числа при выводе таблиц, положение десятичной точки (запятой), количество цифр после десятичной точки и т. д. Для выполнения вывода в классе Console имеются две функции: WriteLine и Write . Они отличаются тем, что функция WriteLine после вывода заданных данных выполняет переход на новую строку, а функция Write этого не делает. Аргументы этих функций состоят из текстовой строки, заключенной в кавычки, после которой (не обязательно) перечисляются имена переменных для вывода. Если выводятся значения переменных, то в строке для каждой переменной в фигурных скобках указывается номер переменной списке.

Обычно номера в строке (в парах фигурных скобок) и позиции переменных в списке совпадают. Возможны также исключения. Например, можно повторить номер какой-либо переменной, тогда ее значение будет выведено повторно. Если же изменить порядок следования чисел в фигурных скобках, то значения переменных будут выводиться именно в этом порядке, а не в порядке записи переменных в списке.

Вместо имен переменных в списке могут использоваться арифметические выражения. Каждому такому выражению должен соответствовать номер в фигурных скобках.

Пусть объявлены переменные: int a = 3, b = 4;

Тогда операторы вывода

Console .WriteLine( «Примеры вывода» ); // Строка формата выводит только текст

// В строке формата нумерация вывода в скобках и номера в списке совпадают

Console .WriteLine( «Обычный порядок: сначала a = <0>потом b = <1>» , a, b);

// В строке формата повтор номера в скобках, 2-я переменная не выводится

Как замкнуть переменную в C# и не выстрелить себе в ногу

Еще в далеком 2005 с выходом стандарта C# 2.0 появилась возможность передачи переменной в тело анонимного делегата посредством ее захвата (или замыкания, кому как угодно) из текущего контекста. В 2008 вышел в свет новый стандарт C# 3.0, принеся нам лямбды, пользовательские анонимные классы, LINQ запросы и многое другое. Сейчас на дворе январь 2020 и большинство C# разработчиков с нетерпением ждут релиз стандарта C# 7.0, который должен привнести много новых полезных «фич». А вот фиксить старые «фичи», никто особо не торопится. Поэтому способов случайно выстрелить себе в ногу по-прежнему хватает. Сегодня мы поговорим об одном из их, и связан он с не совсем очевидным механизмом захвата переменных в тело анонимных функций в языке C#.


Введение

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

А теперь ближе к делу. Я напишу простой пример кода, а вам необходимо будет сказать, что конкретно в данном случае будет выведено в консоль.

И так, приступим:

А теперь внимание, ответ. В консоль будет выведено десять раз число десять:

Эта статья для тех, кто посчитал иначе. Давайте разберёмся в причинах такого поведения.

Почему так происходит?

При объявлении анонимной функции (это может быть анонимный делегат или лямбда) внутри вашего класса, на этапе компиляции будет объявлен еще один класс-контейнер, содержащий в себе поля для всех захваченных переменных и метод, содержащий тело анонимной функции. Для приведенного выше участка кода дизассемблированная структура программы после компиляции будет выглядеть так:

В данном случае метод Foo из приведенного в начале участка кода объявлен внутри класса Program . Для лямбды () => Console.WriteLine(i) компилятором был сгенерирован класс-контейнер c__DisplayClass1_0 , а внутри него — поле i содержащее одноименную захваченную переменную и метод b__0 содержащий тело лямбды.

Давайте рассмотрим дизассемблированный IL код метода b__0 (тело лямбды) с моими комментариями:

Все верно, это именно то, что мы делаем внутри лямбды, никакой магии. Идем дальше.

Как известно, тип int (полное название — Int32 ) является структурой, а значит при передаче куда-либо передается не ссылка на него в памяти, а копируется непосредственно его значение.

Копироваться значение переменной i должно (по логике вещей) во время создания экземпляра класса-контейнера. И если вы ответили неверно на мой вопрос в начале статьи, то вероятнее всего вы ожидали, что контейнер будет создан непосредственно перед объявлением лямбды в коде.

На самом деле переменная i после компиляции вообще не будет создана внутри метода Foo . Вместо этого будет создан экземпляр класса-контейнера c__DisplayClass1_0 , а его поле i будет проинициализировано вместо локальной переменной i значением 0. Более того, везде, где до этого мы использовали локальную переменную i , теперь используется поле класса-контейнера.

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

В итоге мы получаем один экземпляр класса-контейнера на все итерации цикла for . А добавляя при каждой итерации в список actions новую лямбду, мы, по факту, добавляем в него одну и ту же ссылку на ранее созданный экземпляр класса-контейнера. В результате чего, когда мы обходим циклом foreach все элементы списка actions , то все они содержат один и тот же экземпляр класса-контейнера. А если учесть, что цикл for выполняет инкремент к значению итератора после каждой итерации (даже после последней), то значение поля i внутри класса контейнера после выхода из цикла становится равным десяти после выполнения цикла for .

Убедиться во всем мной вышесказанном можно, взглянув на дизассемблированный IL код метода Foo (естественно с моими комментариями):

Вывод

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

Интересный факт заключается в том, что аналогичное поведение было и у цикла foreach до стандарта C# 5.0. Microsoft буквально засыпали жалобами о неинтуитивном поведении в баг-трекере, после чего с выходом стандарта C# 5.0 это поведение было изменено посредством объявления переменной итератора внутри каждой итерации цикла, а не перед ним на этапе компиляции, но для всех остальных конструкций циклов подобное поведение осталось без изменений. Подробнее об этом можно прочитать по ссылке в разделе Breaking Changes .

Вы спросите, как же избежать данной ошибки? На самом деле ответ очень простой. Необходимо следить за тем, где и какие переменные вы захватываете. Помните, класс-контейнер будет создан там, где вы объявили свою переменную, которую в дальнейшем будете захватывать. Если захват происходит в теле цикла, а переменная объявлена за его пределами, то необходимо переприсвоить ее внутри тела цикла в новую локальную переменную. Корректный вариант приведенного в начале примера мог бы выглядеть так:

Если выполнить данный код, то в консоль будут выведены числа от 0 до 9 как и ожидалось:

Посмотрев на IL код цикла for из данного примера, мы увидим, что экземпляр класса-контейнера будет создаваться каждую итерацию цикла. Таким образом, список actions будет содержать ссылки на разные экземпляры с корректными значениями итераторов.

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

Совсем недавно мы — разработчики статического анализатора PVS-Studio — реализовали очередную диагностику, направленную на поиск ошибок неправильного захвата переменных в анонимные функции внутри циклов. В свою же очередь спешу предложить вам проверить ваш код на наличие ошибок и опечаток нашим статическим анализатором.

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

Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

Цукерберг рекомендует:  Советы по правильному использованию html (1-10)
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих