Skip to content

Latest commit

 

History

History
654 lines (494 loc) · 32.7 KB

cs_misc_types.md

File metadata and controls

654 lines (494 loc) · 32.7 KB
Предыдущая лекция   Следующая лекция
Строки. Содержание Общие сведения о подпрограммах.

Перечисления. Множества. Работа с датами. Кортежи.

Перечисления (enum)

Кроме примитивных типов данных в C# есть такой тип как enum или перечисление. Перечисления представляют набор логически связанных констант. Объявление перечисления происходит с помощью оператора enum. Далее идет название перечисления, после которого указывается тип перечисления - он обязательно должен представлять целочисленный тип (byte, int, short, long). Если тип явным образом не указан, то по умолчанию используется тип int. Затем идет список элементов перечисления через запятую:

enum Days
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}
enum Time : byte
{
    Morning,
    Afternoon,
    Evening,
    Night
}

В примерах выше каждому элементу перечисления присваивается целочисленное значение, причем первый элемент будет иметь значение 0, второй 1 и так далее. Мы можем также явным образом указать значения элементов, либо указав значение первого элемента:

enum Operation
{ 
    Add = 1,   // каждый следующий элемент по умолчанию увеличивается на единицу
    Subtract, // этот элемент равен 2
    Multiply, // равен 3
    Divide    // равен 4
}

Но можно и для всех элементов явным образом указать значения:

enum Operation
{ 
    Add = 2,
    Subtract = 4,
    Multiply = 8,
    Divide = 16
}

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

enum Color
{
    White = 1,
    Black = 2,
    Green = 2,
    Blue = White // Blue = 1
}

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

enum Operation
{
    Add = 1,
    Subtract,
    Multiply,
    Divide
}
class Program
{
    static void Main(string[] args)
    {
        Operation op;
        op = Operation.Add;
        Console.WriteLine(op); // Add
         
        Console.ReadLine();
    }  
}

В программе мы можем присвоить значение этой переменной. При этом в качестве ее значения должна выступать одна из констант, определенных в данном перечислении. То есть несмотря на то, что каждая константа сопоставляется с определенным числом, мы не можем присвоить ей числовое значение, например, Operation op = 1;. И также если мы будем выводить на консоль значение этой переменной, то мы получим имя константы, а не числовое значение. Если же необходимо получить числовое значение, то следует выполнить приведение к числовому типу:

Operation op;
op = Operation.Multiply;
Console.WriteLine((int)op); // 3

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

class Program
{
    enum Operation
    {
        Add = 1,
        Subtract,
        Multiply,
        Divide
    }
 
    static void MathOp(double x, double y, Operation op)
    {
        double result = 0.0;
 
        switch (op)
        {
            case Operation.Add:
                result = x + y;
                break;
            case Operation.Subtract:
                result = x - y;
                break;
            case Operation.Multiply:
                result = x * y;
                break;
            case Operation.Divide:
                result = x / y;
                break;
        }
 
        Console.WriteLine("Результат операции равен {0}", result);
    }
 
    static void Main(string[] args)
    {
        // Тип операции задаем с помощью константы Operation.Add
        MathOp(10, 5, Operation.Add);
        // Тип операции задаем с помощью константы Operation.Multiply
        MathOp(11, 5, Operation.Multiply);
             
        Console.ReadLine();
    }  
}

Кортежи (Tuple)

Кортежи предоставляют удобный способ для работы с набором значений.

Кортеж представляет набор значений, заключенных в круглые скобки:

var tuple = (5, 10);

В данном случае определен кортеж tuple, который имеет два значения: 5 и 10. В дальнейшем мы можем обращаться к каждому из этих значений через поля с названиями Item[порядковый_номер_поля_в_кортеже]. Например:

static void Main(string[] args)
{
    var tuple = (5, 10);
    Console.WriteLine(tuple.Item1); // 5
    Console.WriteLine(tuple.Item2); // 10
    tuple.Item1 += 26;
    Console.WriteLine(tuple.Item1); // 31
    Console.Read();
}

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

(int, int) tuple = (5, 10);

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

(string, int, double) person = ("Tom", 25, 81.23);

Первый элемент кортежа в данном случае представляет строку, второй элемент - тип int, а третий - тип double.

Мы также можем дать названия полям кортежа:

var tuple = (count:5, sum:10);
Console.WriteLine(tuple.count); // 5
Console.WriteLine(tuple.sum); // 10

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

Мы даже можем не использовать переменную для определения всего кортежа, а использовать отдельные переменные для его полей:

static void Main(string[] args)
{
    var (name, age) = ("Tom", 23);
    Console.WriteLine(name);    // Tom
    Console.WriteLine(age);     // 23
    Console.Read();
}

Использование кортежей

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

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

static void Main(string[] args)
{
    var tuple = GetValues();
    Console.WriteLine(tuple.Item1); // 1
    Console.WriteLine(tuple.Item2); // 3
     
    Console.Read();
}
private static (int, int) GetValues()
{
    var result = (1, 3);
    return result;
}

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

Другой пример:

static void Main(string[] args)
{
    var tuple = GetNamedValues(new int[]{ 1,2,3,4,5,6,7});
    Console.WriteLine(tuple.count);
    Console.WriteLine(tuple.sum);
     
    Console.Read();
}
private static (int sum, int count) GetNamedValues(int[] numbers)
{
    var result = (sum:0, count: 0);
    for (int i=0; i < numbers.Length; i++)
    {
        result.sum += numbers[i];
        result.count++;
    }
    return result;
}

И также кортеж может передаваться в качестве параметра в метод:

static void Main(string[] args)
{
    var (name, age) = GetTuple(("Tom", 23), 12);
    Console.WriteLine(name);    // Tom
    Console.WriteLine(age);     // 35
    Console.Read();
}
         
private static (string name, int age) GetTuple((string n, int a) tuple, int x)
{
    var result = (name: tuple.n, age: tuple.a + x);
    return result;
}

Множества

Коллекция, содержащая только отличающиеся элементы, называется множеством (set). В составе .NET имеются два множества — HashSet и SortedSet. Оба они реализуют интерфейс ISet. Класс HashSet содержит неупорядоченный список различающихся элементов, а в SortedSet элементы упорядочены.

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

Ниже перечислены наиболее употребительные конструкторы, определенные в классе HashSet:

  • public HashSet ()
  • public HashSet(IEnumerable collection)
  • public HashSet(IEqualityCompare comparer)
  • public HashSet(IEnumerable collection, IEqualityCompare comparer)

В первой форме конструктора создается пустое множество, а во второй форме — множество, состоящее из элементов указываемой коллекции collection. В третьей форме конструктора допускается указывать способ сравнения с помощью параметра comparer. А в четвертой форме создается множество, состоящее из элементов указываемой коллекции collection, и используется заданный способ сравнения comparer. Имеется также пятая форма конструктора данного класса, в которой допускается инициализировать множество последовательно упорядоченными данными.

В этом классе предоставляется также метод RemoveWhere(), удаляющий из множества элементы, удовлетворяющие заданному условию, или предикату. Помимо свойств, определенных в интерфейсах, которые реализуются в классе HashSet, в него введено дополнительное свойство Comparer, приведенное ниже:

public IEqualityComparer<T> Comparer { get; }

Оно позволяет получать метод сравнения для вызывающего хеш-множества.

Ниже перечислены четыре наиболее часто используемых конструкторов, определенных в классе SortedSet:

  • public SortedSet()
  • public SortedSet(IEnumerable collection)
  • public SortedSet(IComparer comparer)
  • public SortedSet(IEnumerable collection, IComparer comparer)

В первой форме конструктора создается пустое множество, а во второй форме — множество, состоящее из элементов указываемой коллекции collection. В третьей форме конструктора допускается указывать способ сравнения с помощью параметра comparer. А в четвертой форме создается множество, состоящее из элементов указываемой коллекции collection, и используется заданный способ сравнения comparer. Имеется также пятая форма конструктора данного класса, в которой допускается инициализировать множество последовательно упорядоченными данными.

В этом классе предоставляется также метод GetViewBetween(), возвращающий часть множества в форме объекта типа SortedSet, метод RemoveWhere(), удаляющий из множества элементы, не удовлетворяющие заданному условию, или предикату, а также метод Reverse(), возвращающий объект типа IEnumerable, который циклически проходит множество в обратном порядке.

Помимо свойств, определенных в интерфейсах, которые реализуются в классе SortedSet, в него введены дополнительные свойства, приведенные ниже:

  • public IComparer Comparer { get; }
  • public T Max { get; }
  • public T Min { get; }

Свойство Comparer получает способ сравнения для вызывающего множества. Свойство Мах получает наибольшее значение во множестве, а свойство Min — наименьшее значение во множестве. Давайте рассмотрим пример использования множеств:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            // Создадим два множества
            SortedSet<char> ss = new SortedSet<char>();
            SortedSet<char> ss1 = new SortedSet<char>();

            ss.Add('A');
            ss.Add('B');
            ss.Add('C');
            ss.Add('Z');
            ShowColl(ss, "Первая коллекция: ");

            ss1.Add('X');
            ss1.Add('Y');
            ss1.Add('Z');
            ShowColl(ss1, "Вторая коллекция");

            ss.SymmetricExceptWith(ss1);
            ShowColl(ss,"Исключили разноименность (одинаковые элементы) двух множеств: ");

            ss.UnionWith(ss1);
            ShowColl(ss, "Объединение множеств: ");

            ss.ExceptWith(ss1);
            ShowColl(ss, "Вычитание множеств");

            Console.ReadLine();
        }

        static void ShowColl(SortedSet<char> ss, string s)
        {
            Console.WriteLine(s);
            foreach (char ch in ss)
                Console.Write(ch + " ");
            Console.WriteLine("\n");
        }
    }
}

Самописанный пример, объединяющий работу перечислений и множеств:

class Program
{
    enum Days
    {
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday
    }

    static void Main(string[] args)
    {
        HashSet<Days> DaysHashSet = new HashSet<Days>();
        DaysHashSet.Add(Days.Friday);
        DaysHashSet.Add(Days.Friday);
        DaysHashSet.Add(Days.Monday);

        foreach (Days DayItem in DaysHashSet)
            Console.WriteLine(DayItem);

        Console.Write("Press ENTER to continue...");
        Console.ReadLine();
    }
}

Программа выведет только Friday и Monday. Т.е. дубли в множество не добавляются и при этом никаких ошибок не возникает. Такое поведение может быть полезно при отслеживании состояния какого-то устройства. Периодически опрашивая устройство мы можем устанавливать состояния не проверя установлено уже это состояние или нет.

Для коллекций вообще и для множеств в частности есть методы объединения, пересечения и разности.

Разность последовательностей

С помощью метода Except можно получить разность двух последовательностей:

var DaysHashSet1 = new HashSet<Days>() { Days.Friday, Days.Monday };
var DaysHashSet2 = new HashSet<Days>() { Days.Friday, Days.Sunday };

var ResHashSet = DaysHashSet1.Except(DaysHashSet2);

foreach (Days DayItem in ResHashSet)
    Console.WriteLine(DayItem);

Console.Write("Press ENTER to continue...");
Console.ReadLine();

В данном случае из множества DaysHashSet1 убираются все элементы, которые есть в массиве DaysHashSet2. Результатом операции будет один элемент:

Monday

Пересечение последовательностей

Для получения пересечения последовательностей, то есть общих для обоих наборов элементов, применяется метод Intersect:

string[] soft = { "Microsoft", "Google", "Apple"};
string[] hard = { "Apple", "IBM", "Samsung"};
 
// пересечение последовательностей
var result = soft.Intersect(hard);
 
foreach (string s in result)
    Console.WriteLine(s);

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

Apple

Объединение последовательностей

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

string[] soft = { "Microsoft", "Google", "Apple"};
string[] hard = { "Apple", "IBM", "Samsung"};
 
// объединение последовательностей
var result = soft.Union(hard);
 
foreach (string s in result)
    Console.WriteLine(s);

Результатом операции будет следующий набор:

Microsoft
Google
Apple
IBM
Samsung

Если же нам нужно простое объединение двух наборов, то мы можем использовать метод Concat:

var result = soft.Concat(hard);

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

Удаление дубликатов

Для удаления дублей в наборе используется метод Distinct:

var result = soft.Concat(hard).Distinct();

Последовательное применение методов Concat и Distinct будет подобно действию метода Union.


Работа с датами и временем

Для работы с датами и временем в .NET предназначена структура DateTime. Она представляет дату и время от 00:00:00 1 января 0001 года до 23:59:59 31 декабря 9999 года.

Для создания нового объекта DateTime также можно использовать конструктор. Пустой конструктор создает начальную дату:

DateTime date1 = new DateTime();
Console.WriteLine(date1); // 01.01.0001 0:00:00

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

Console.WriteLine(DateTime.MinValue);

Чтобы задать конкретную дату, нужно использовать один из конструкторов, принимающих параметры:

DateTime date1 = new DateTime(2015, 7, 20); // год - месяц - день
Console.WriteLine(date1); // 20.07.2015 0:00:00

Установка времени:

DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25); // год - месяц - день - час - минута - секунда
Console.WriteLine(date1); // 20.07.2015 18:30:25

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

Console.WriteLine(DateTime.Now);
Console.WriteLine(DateTime.UtcNow);
Console.WriteLine(DateTime.Today);

Консольный вывод:

20.07.2015 11:43:33
20.07.2015 8:43:33
20.07.2015 0:00:00

Свойство DateTime.Now берет текущую дату и время компьютера, DateTime.UtcNow- дата и время относительно времени по Гринвичу (GMT) и DateTime.Today - только текущая дата.

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

DateTime someDate = new DateTime(1582, 10, 5);
Console.WriteLine(someDate.DayOfWeek);

Консоль выстветит значение Tuesday, то есть вторник. Однако, как может быть известно из истории, впервые переход с юлианского календаря на григорианский состоялся в октябре 1582 года. Тогда после даты 4 октября (четверг) (еще по юлианскому календарю) сразу перешли к 15 октября (пятница)(уже по григорианскому календарю). Таким образом, фактически выкинули 10 дней. То есть после 4 октября шло 15 октября.

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

Операции с DateTime

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

Для добавления дат используется ряд методов:

  • Add(TimeSpan value): добавляет к дате значение TimeSpan

  • AddDays(double value): добавляет к текущей дате несколько дней

  • AddHours(double value): добавляет к текущей дате несколько часов

  • AddMinutes(double value): добавляет к текущей дате несколько минут

  • AddMonths(int value): добавляет к текущей дате несколько месяцев

  • AddYears(int value): добавляет к текущей дате несколько лет

Например, добавим к некоторой дате 3 часа:

DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25); // 20.07.2015 18:30:25
Console.WriteLine(date1.AddHours(3)); // 20.07.2015 21:30:25

Для вычитания дат используется метод Substract(DateTime date):

DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25); // 20.07.2015 18:30:25
DateTime date2 = new DateTime(2015, 7, 20, 15, 30, 25); // 20.07.2015 15:30:25
Console.WriteLine(date1.Subtract(date2)); // 03:00:00

Здесь даты различаются на три часа, поэтому результатом будет дата "03:00:00".

Метод Substract не имеет возможностей для отдельного вычитания дней, часов и так далее. Но это и не надо, так как мы можем передавать в метод AddDays() и другие методы добавления отрицательные значения:

// вычтем три часа
DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25);  // 20.07.2015 18:30:25
Console.WriteLine(date1.AddHours(-3)); // 20.07.2015 15:30:25

Кроме операций сложения и вычитания еще есть ряд методов форматирования дат:

DateTime date1 = new DateTime(2015, 7, 20, 18, 30, 25);
Console.WriteLine(date1.ToLocalTime()); // 20.07.2015 21:30:25
Console.WriteLine(date1.ToUniversalTime()); // 20.07.2015 15:30:25
Console.WriteLine(date1.ToLongDateString()); // 20 июля 2015 г.
Console.WriteLine(date1.ToShortDateString()); // 20.07.2015
Console.WriteLine(date1.ToLongTimeString()); // 18:30:25
Console.WriteLine(date1.ToShortTimeString()); // 18:30

Метод ToLocalTime() преобразует время UTC в локальное время, добавляя смещение относительно времени по Гринвичу. Метод ToUniversalTime(), наоборот, преобразует локальное время во время UTC, то есть вычитает смещение относительно времени по Гринвичу. Остальные методы преобразуют дату к определенному формату.

Метод ToString() тоже можно использовать для форматирования. По умолчанию для русской культурной среды он возвращает дату в формате ДД.ММ.ГГГГ Ч:ММ:СС. Но можно задать нужный формат в параметрах: дата.ToString("dd.MM.yyyy HH:mm:ss");

Описатель Описание
d Представляет день месяца от 1 до 31. Одноразрядные числа используются без нуля в начале
dd Представляет день месяца от 1 до 31. К одноразрядным числам в начале добавляется ноль
ddd Сокращенное название дня недели (пн,вт...)
dddd Полное название дня недели (понедельник, вторник...)
f / fffffff Представляет миллисекунды. Количество символов f указывает на число разрядов в миллисекундах
g Представляет период или эру (например, "н. э.")
h Часы в виде от 1 до 12. Часы с одной цифрой не дополняются нулем
hh Часы в виде от 01 до 12. Часы с одной цифрой дополняются нулем
H Часы в виде от 0 до 23. Часы с одной цифрой не дополняются нулем
HH Часы в виде от 0 до 23. Часы с одной цифрой дополняются нулем
K Часовой пояс
m Минуты от 0 до 59. Минуты с одной цифрой не дополняются начальным нулем
mm Минуты от 0 до 59. Минуты с одной цифрой дополняются начальным нулем
M Месяц в виде от 1 до 12
MM Месяц в виде от 1 до 12. Месяц с одной цифрой дополняется начальным нулем
MMM Сокращенное название месяца
MMMM Полное название месяца
s Секунды в виде числа от 0 до 59. Секунды с одной цифрой не дополняются начальным нулем
ss Секунды в виде числа от 0 до 59. Секунды с одной цифрой дополняются начальным нулем
t Первые символы в обозначениях AM и PM
tt AM или PM
y Представляет год как число из одной или двух цифр. Если год имеет более двух цифр, то в результате отображаются только две младшие цифры
yy Представляет год как число из одной или двух цифр. Если год имеет более двух цифр, то в результате отображаются только две младшие цифры. Если год имеет одну цифру, то он дополняется начальным нулем
yyy Год из трех цифр
yyyy Год из четырех цифр
yyyyy Год из пяти цифр. Если в году меньше пяти цифр, то он дополняется начальными нулями
z Представляет смещение в часах относительно времени UTC
zz Представляет смещение в часах относительно времени UTC. Если смещение представляет одну цифру, то она дополняется начальным нулем.

Предыдущая лекция   Следующая лекция
Строки. Содержание Общие сведения о подпрограммах.