Безумно короткий самоучитель по C#. Часть 1 из 2.
Этот самоучитель:
- Без разжевывания
- Поверхностный
- Не полный
Если хотите - жуйте подробности в комменты, задавайте вопросы где "совсем не понятно".
Материал чутка связан, потому если вы встретили в коде что-то непонятное - читайте дальше, наверняка будет ответ.
Прошу прощения за очень грубое и поверхностное объяснение. На самом деле именно в нем и есть соль, что знания можно "подхватить". А как вы с ними будете работать дальше другой вопрос. Так же как и возможные конструкции - не все.
Итак, поехали:
Типы
C#, это не паскаль, там главное - типы.
Типы очень грубо делятся на:
- типы значений (структуры, struct)
- ссылочные типы (классы, class)
- интерфейсы (interface)
- перечисления (enum, но под капотом они сводятся к struct)
Есть несколько базовых типов. Самые часто юзаемые это:
- int (целое число) //Пример 0, 10, 10i
- float (дробное число) //Пример 0.1, .1f, 10f
- char (символ) //Пример 'a', 'b', 'c'
- string (строка, массив символов) //Пример "строка", @"другая строка"
- bool (true/false, да/нет, переключатель) //Пример true, false
- byte (байт, по сути тоже целое число, но с маленьким диапозоном)
- double (дробное число поточнее)
- object (общий тип)
Свои же типы состоят из вот этих базовых и других созданных типов:
class Яблоко
{
Color цвет;
float вес;
}
Чем типы значений отличаются от ссылочных типов?
В первом случае (типы значений) идет сравнение объектов по содержимому.
То есть из примера выше - два яблока будут равны, если их цвет и вес совпадет.
Во втором случае (ссылочные типы) идет сравнение по ссылке.
То есть два яблока будут равны только если это одно и тоже яблоко, независимо от того, совпадает их содержимое или нет.
Инкапсуляция
Инкапсуляция обеспечивает уровень доступа какой-то сущности (типа, метода, поля, свойства) и выражается одним из четырех ключевых слов.
- public (обшедоступно)
- private (можно использовать только внутри класса)
- internal (можно использовать только внутри текущей сборки/библиотеки)
- protected (можно использовать только внутри класса или в классе-наследнике)
- protected internal (можно использовать внутри текущей сборки/библиотеки хоть где, а вне сборки только для классов-наследников)
Эти слова пишутся соответственно перед инициализацией всех классов/полей/методов
То есть:
public class Яблоко
{
public Color цвет;
internal float вес;
}
Если ключевое слово у поля отсутствует, то оно по умолчанию считается за private.
Наследование
Наследование помогает не переписывать общие данные внутри классов 20 раз.
Пишется через двоеточие после названия класса (указываются классы/интерфейсы).
То есть:
public class Фрукт
{
public Color цвет;
internal float вес;
}
public class Яблоко : Фрукт
{
public bool червивое;
}
public class Томат : Фрукт
{
public int количествоСемян;
}В этом случае и яблоки и томат будут иметь общие поля фруктов - цвет и вес.
Класс может пронаследоваться максимум от одного другого класса.
Любой тип, в том числе класс может реализовывать любое количество интерфейсов.
Полиморфизм
Полиморфизм это свойство объекта представляться любым из пронаследованных типов.
Например из примера выше мы можем описать поля так:
public class MyClass
{
public Фрукт фрукт; //В такое поле мы можем положить любой фрукт, будь он хоть яблоко, хоть груша
public Яблоко яблоко; //Сюда можно положить только яблоки
public object чтоУгодно; //В поле с типом object мы можем положить что угодно, это общий тип у всего
}Метод
Метод, функция, процедура - почти синонимы.
Процедура - метод, который ничего не возвращает.
Функция - метод, который что-то возвращает.
Метод - функция/процедура.
В сути же своей в разговорной речи как вы не называйте это - все вас прекрасно поймут. В данной статье я возможно даже пару раз перепутал эти понятия.
В методах вы можете писать специфичный для вашего объекта код.
Например:
public class Яблоко
{
private bool червивое;
public void СделатьЧервивым()
{
червивое = true;
}
} Там где указан 'void' подставляется возвращаемый тип. Если функция просто выполняется и не должна отдавать никакое значение, ставится void
Пример функции возвращающей значение:
public class Яблоко
{
private bool червивое;
public bool ПроверитьЧервивость()
{
return червивое;
}
}'return' возвращает значение переменной.
После выполнения 'return' функция обрывается, код при срабатывание 'return' не выполняется дальше.
В круглых скобках указываются параметры. Например:
public class Яблоко
{
private bool червивое;
private float вес;
public void НазначитьСвойства(bool червивое, float вес)
{
this.червивое = червивое;
this.вес = вес;
}
}Слово this позволяет обратиться к полям экземпляра, когда в самой функции уже есть переменные с таким названием.
Конструктор и деструктор
Для создания объекта вы можете использовать специальный метод - конструктор, а при очистке срабатывает метод деструктор
Конструктор не указывает возвращаемый тип - тупо пишется название типа, а затем параметры.
public class Яблоко
{
public float вес;
public Яблоко(float вес)
{
this.вес = вес;
}
}Такой тип можно создать c помощью ключевого слова new в любом методе
var яблоко = new Яблоко(.25f);
Если у класса не указаны конструкторы то считается что у него есть пустой конструктор без параметров. Такой класс можно создать вот так:
var яблоко = new Яблоко();
Деструктор выполняется когда объект уничтожается. Объект уничтожается автоматически если нигде не используется и не хранится.
public class Яблоко
{
~Яблоко()
{
Debug.Log("Яблоко съедено!");
}
}Инициализация
Помимо использования конструктора вы можете указать для полей значения по умолчанию прямо после объявления.
public class Яблоко
{
public float вес = .2f;
}Свойства
Свойство это гибрид поля и метода. Он нужен когда при присвоении значения или его возврате нужно выполнить какие-то дополнительные операции. Так же хорошим тоном считается помечать как поля кешируемые переменные.
По сути свойства это синтаксический сахар - можно обойтись без них, но они удобные.
Развернутый вид выглядит например вот так:
public class Яблоко
{
private float _вес;
public float вес
{
get { return _вес; }
private set { _вес = value; }
}
} private float _вес;
public float get_Weight
{
return _вес;
}
private void set_Weight (float value)
{
_вес = value;
}Ключевое слово value позволяет присвоить значение.
Данный пример показывает как свойство "вес" можно узнать откуда угодно, но изменить только внутри класса.
Функция set может отсутствовать вовсе:
public class Яблоко
{
private float _вес;
public float вес
{
get { return _вес; }
}
}Помимо развернутой конструкции есть "свернутый вариант":
public class Яблоко
{
public float вес { get; set; }
public float вес2 { get; private set; }
}В свернутом варианте мы не можем указать доп код и использовать поле само по себе, но он удобен когда нам просто нужно изменить уровень доступа.
Статичность
Бывает случаи когда какая-то часть класса относится не к одному объекту, а ко всем сразу.
Такие сущности помечаются словом static
public class Яблоко
{
public Color цвет;
public Яблоко()
{
всеЯблоки.Add(this);
}
~Яблоко()
{
всеЯблоки.Remove(this);
}
public static List<Яблоко> всеЯблоки = new List<Яблоко>;
public static void УдалитьВсеЯблоки()
{
всеЯблоки.Clear();
}
}Такие сущности существуют не на уровне экземпляра, а на уровне класса.
Вы так же можете помечать классы как статичные. В этом случае внутри смогут быть только статичные поля и функции, а сам класс нельзя будет создать.
Для сравнения
Доступ к полю экземпляра "цвет":
var яблоко = new Яблоко(); яблоко.цвет = Color.green;
Доступ к статичному полю "всеЯблоки":
Яблоко.всеЯблоки.Clear();
Абстрактность, перезапись, интерфейсы
Бывает нужным, чтобы наследники обязательно реализовывали какой-то метод.
Есть несколько способов этого достичь.
1 способ.
Пометить класс как абстрактный.
public abstract class Фрукт
{
public abstract void СьестьФрукт(); //Никаких фигурных скобок!
}
public class Яблоко : Фрукт
{
public void СьестьФрукт() //Если класс не будет содержать такой метод то компилятор выведет ошибку
{
//Какой то код
}
}Абстрактный класс нельзя создать через new. Только его наследников.
2 способ.
Пометить поля как возможные к перезаписи.
public class Фрукт
{
public virtual void СьестьФрукт() //Помечаем словом virtual
{
}
}
public class Яблоко : Фрукт
{
public override void СьестьФрукт() //Помечаем словом override
{
//Какой то код
}
}При перезаписи вы можете вызвать метод-родитель
Например
public class Фрукт
{
public virtual void СьестьФрукт() //Помечаем словом virtual
{
//Здесь какой нибудь код
}
}
public class Яблоко : Фрукт
{
public override void СьестьФрукт() //Помечаем словом override
{
base.СьестьФрукт(); //Выполнить код у родителя
//Какой то другой код
}
}Такой подход не выдаст ошибку если наследуемый класс не реализует метод. В этом случае выполнится код класса, от которого пронаследовались.
3 способ.
Интерфейсы - это такая конструкция, которая обязывает вас реализовывать методы.
По сути работает так же как абстрактный класс, но не позволяет создавать поля и указывать уровень доступа (он всегда считается либо как паблик, либо видим только на уровне интерфейса). Можно реализовать несколько интерфейсов на один класс.
Пример:
public interface IФрукт
{
void СьестьФрукт();
}
public class Яблоко : IФрукт
{
public void СьестьФрукт() //Если класс не будет содержать такой метод то компилятор выведет ошибку
{
}
}Код алгоритмов
Код алгоритмов здесь (такого понятия вообще т нет) - это код который выполняется ВНУТРИ функции. Ниже речь пойдет о том что там можно делать.
Объявление переменной
Переменные можно создавать в любой части кода.
Класс не помеченный как статичный способен создавать экземпляры. Создание переменной происходит с помощью слова new
Яблоко яблоко = new Яблоко();
В этом примере мы указываем явный тип "Яблоко" для переменной "яблоко" и затем создает объект с помощью дефолтного конструктора без параметров.
В таких очевидных случаях возможно не указывать тип явно, а просто подставить ключевое слово 'var'
var яблоко = new Яблоко();
Однако в некоторых случаях указать тип нужно, например когда мы хотим представить объект в другом типе
Фрукт яблоко = new Яблоко();
Так же можно объявлять переменные полей и свойств. Например:
public class Рука
{
public object предметВРуке;
public void ВзятьЯблоко()
{
предметВРуке = new Яблоко();
}
}Значения по умолчанию
Так же есть значение null обозначающее "ничего". Используется, например, для очистки значений.
null могут присвоить только интерфейсы, классы или объект типа object.
Фрукт фрукт = new Яблоко(); фрукт = null; //Теперь фрукт равен null
При инициализации переменных у них есть значения по умолчанию. Например вот такая конструкция:
Фрукт фрукт; float вес;
фрукт в этом случае равен null, так как это ссылочный тип и значение не было указано.
вес равен 0, так как типы значений не могут быть равны null.
Обращение к полям и свойствам
Вы можете обратиться к полям и свойствам любого объекта:
Яблоко яблоко = new Яблоко(); яблоко.вес = .1f; //Обращение к полю 'вес'
К статичным полям обращаются по названию класса:
Яблоко.всеЯблоки = null;
Обращение к методам
яблоки.Add(new Яблоко());
Условия, циклы, контекст
Условия и циклы всем известные конструкции, объяснять их смысл, думаю, не шибко нужно.
Примеры условий:
if (условие)
{
//Действия
}
if (условие)
{
//Действия
}
else
{
//Действия
}
if (условие)
{
//Действия
}
else if (условие)
{
//Действия
}Примеры циклов:
while (условие)
{
//действия
}
do
{
//действия
}
while (условие);
for (int i = 0; i < 4; i++)
{
}В качестве условия выступает любое выражение, возвращающее тип bool (true/false)
Любой цикл можно прервать командой 'break' и продолжить выполнение действий дальше, после цикла.
Можно начать "новый виток цикла" командой 'continue'
Внутри круглых скобок цикл for делится на три части
- Выполняется перед стартом цикла
- Условие выхода из цикла
- Действие в конце каждого цикла
Если после условия/цикла не стоит фигурных скобок (контекста) значит будет выполнено следующее действие после конструкции. Например:
if (условие) //Действие
Переменные, созданные в рамках контекста, если никуда не записываются - уничтожаются по окончанию контекста. Переменные "живут" текущим контекстом или контекстом выше.
Пример
if (условие)
{
var яблоко = new Яблоко();
{
яблоко.вес = .1f; //Здесь нет ошибки, так как текущий контекст существует внутри контекста, где переменная существует
}
}
яблоко.вес = .1f; //Здесь ошибка, так как переменная "яблоко" создана в другом контексте.Массивы и списки
Массив описывается квадратными скобками. Например
Яблоко[] яблоки = null;
Объявление массива происходит с указанием длины массива:
Яблоко[] яблоки = new Яблоко[10];
Есть так же прямоугольные и зубчатые массивы.
Пример прямоугольного массива:
float[,] матрица4х4 = new float[4,4];
Это означает что будет создан массив 4 на 4 (как таблица). Количество измерений может быть любым
Пример зубчатого массива:
var зубчатыйМассив = new Яблоко[10][];
Это значит, что первое измерение имеет длину 10. Вторая размерность определяется для каждого объекта персонально. Например:
var зубчатыйМассив = new Яблоко[10][]; зубчатыйМассив[0] = new Яблоко[12];
Если вы не знаете точной длины вы можете использовать списки. Они имеют динамичную длину. Списки представлены классом List<T>
Пример:
var список = new List<Яблоко>(); список.Add(new Яблоко());
Обычный массив - array.Length
Прямоугольный массив - array.GetLength(n) для длины конкретного измерения
Список - list.Count
Объявление с контекстом
Во время объявления можно заюзать фигурные скобки чтобы указать присвоение каких-то данных. Это чисто синтаксический сахар.
Пример:
var список = new List<Яблоко> { new Яблоко(), new Яблоко() };
var массив = new [] { new Яблоко(), new Яблоко() };
var объект = new Яблоко { вес = 0.1f };Операторы сравнения
В C# есть следующие операторы для сравнения (в примере):
a == b //равно a != b //не равно a >= b //больше либо равно a <= b //меньше либо равно a > b //больше a < b //меньше
Используются для получения выражений типа bool, в том числе условий
Арифметические операторы
a + b a * b a - b a / b a % b //Остаток от деления
Операторы присвоения
Есть следующие операторы присвоения (в примере):
a = b; //Присвоить а значение b a += 2; //Увеличить a на 2, аля a = a + 2; a -= 2; a *= 2; a /= 2; a %= 2;
Приведение к типу
Вы можете привести объект к определенному типу
Пример конструкций:
var яблоко = new Яблоко(); var фрукт = (Фрукт)яблоко; //Приводим к типу "Фрукт". В случае невозможности привести тип - ошибка var фрукт = яблоко as Фрукт; //Приводим к типу "Фрукт". В случае невозможности привести тип - запишется null. Можно делать только с типами поддерживающими null-значение. var этоФрукт = яблоко is Фрукт; //Проверяем, можно ли привести объект к типу "фрукт". Возвращает true или false
Инкремент и декремент
Выглядят они например вот так: i++, i--, ++i, --i.
По сути это вычитание или прибавление единицы к любой числовой переменной.
Разница в порядке выполнения на примере (надеюсь не намудил):
var i = 1; var a = i++; //Сначала присвоить a = i, затем прибавить i на 1. В итоге a = 1, i = 2 var i = 1; var a = ++i; //Сначала прибавить i на 1, затем присвоить a = i. В итоге a = 2, i = 2 i++; //Просто прибавить i на 1 ++i; //Просто прибавить i на 1
Что я упустил из основ и сам знаю что это упустил:
- дженерик типы
- ref/out
- пространства имен
- методы расширения
- using
- лямбда-выражения
- делегаты
- события
- рефлексия
- операторы в классах
- индексаторы
Возможно, в следующей серии, если эта статья затащит ;)
Код строчил в блокноте, если где-то намудил - исправляйтеСмотрите также:
Комментарии
Кет, ну "размер" путается с "измерением" чутка. А длина - синоним слова, фактически это и есть правильное название, ибо поле для доступа к этим данным называется "Length" все-таки, а не "Size". Чтобы не было двусмысленно - пусть будет длиной.
Кет, длина чего-то - это размер чего-то по данному измерению. Ну и size обычно в <prefix>байтах и определен не только для массивов.
"[n,m]" - размер. "n" - длина по первому измерению, "m" - по второму.
Когда не о чем спорить, спорят о предпочтительности той или иной терминологии.
:)
Mihahail, ну, по делу мне правда нечего сказать — не знаком с C#.
Но аргументы понял и согласен. Спасибо, что разъяснили.
Статья тащит, ждем вторую часть.


<a href= http://mosros.flybb.ru/viewtopic.php?f=2&t=635>Процесс получения диплома стоматолога: реально ли это сделать быстро?</a>