Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Работа с перечислениями Enum




Лекция 8

Продвинутое программирование. Интерфейсы

Приведение и преобразование типов

  Существуют два типа приведения типов – явное и неявное.

 При неявном приведении к переменной осуществляется обращение, а она приводится к другому типу. Пример:

  int i = 10;

  object obj = i;

В первой строке объявляется числовая переменная, а во второй строке объектной переменной obj присваивается значение числовой переменной. Это свойство объектно-ориентированного программирования означает, что можно присваивать переменным-предкам значения их потомков, но от этого предками они не становятся.

В большинстве случаев при попытке неявного приведения типов в C# компилятор выдаст сообщение об ошибке. Там, где создаётся впечатление, что используется неявное приведение, чаще всего используется какой-то перегруженный метод.

  При явном приведении перед переменной в скобках указывают имя нового типа. Пример:

  int  c = (int) obj;

  В данном случае приведение типов обусловливается тем, что в переменной хранится число, но нам передали его в виде объекта object. Приведением можно показать, что в объектной переменной на самом деле хранится число. Если в obj не будет находиться число, компилятор выдаст сообщение об ошибке. Следовательно, необходимо быть уверенным, что переменная имеет нужный нам тип; с помощью типа данных в скобках программист сообщает компилятору, чтобы obj нужно воспринимать как число.

Если нужно выполнить преобразование типов, то для этого можно использовать класс Convert. Например, если необходимо преобразовать какую-то переменную в тип даты и времени DateTime, то нужно использовать метод ToDateTime():

  DateTime dt = Convert.ToDateTime(переменная);

Данный метод реализован для 18 различных типов, и любой из этих типов он будет «пробовать» привести к типу DateTime.

Если данные какого-либо типа необходимо привести к строке string, для этого в классе Convert предусмотрен метод ToString() (также с 18 перегруженными вариантами), для преобразования в тип int (Int32) используется метод ToInt32() и т. д.

В следующем примере

int i = 10;

string s = "" + i;

во второй строке кода произойдет неявное для программиста, но явное для компилятора преобразование типов. Переменная i будет преобразована к строке с помощью вызова метода ToString(), затем произойдёт конкатенация строк. Так как необходимо привести тип к строке, то среда разработки выполнения и компилятор достаточно «интеллектуальны», чтобы преобразовывать его автоматически. Обратное преобразование строки в число таким способом невозможно, в этом случае нужно использовать Convert.

Типы данных в .NET – объекты

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

 

 

  Здесь действует так называемый механизм автоупаковки. Переменные типа int, double и т. д. являются псевдонимами для классов, и когда нужно произвести с ними математические операции, система воспринимает их как простые типы данных.

  Чтобы удобнее было работать с типами данных, как с классами, в .NET также существуют специализированные классы для типов данных, их имена начинаются с большой буквы: Int16, Int32, Int64, Double, String и т. д. У этих классов есть множество статических методов. Например:

  string sNumber = "10";

  int iNumber = Int32.Parse(sNumber);

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

 

Работа с перечислениями Enum

      Перечисления в языках C и C# можно считать способом создания собственных удобных типов данных. Допустим, что нам нужно иметь переменную, в которой нужно сохранить текущий день недели. Как это можно сделать? Можно представить его строкой, но это будет не сам день недели, а название дня недели. Можно представить его числом от 1 до 7, но это будет номер дня, но не день недели. Как раз для подобных случаев предусмотрены перечисления, синтаксис объявления которых (в общем виде) выглядит следующим образом:

  enum имя {значения через запятую};

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

  enum WeekDays {Monday, Tuesday, Wednesday, Thursday, Friday,  

  Saturday, Sunday};

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

  WeekDays day;

  day = WeekDays.Thursday;

  В первой строке объявлена переменная day типа WeekDays. Во второй строке переменной присваивается значение четверга.

  При написании программ на C# перечисления могут объявляться как вне класса, так и внутри объявления класса. В первом случае перечисление будет доступно для всех классов данного пространства имен, а при наличии модификатора public и всем сторонним классам. Любой класс сможет объявить переменную данного типа перечисления.

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

  namespace EnumIndex

  {

     public partial class Form1 : Form

     {

         public enum MyColors {Red, Green, Blue};

         ...

     }

     public class Test

     {

         Form1.MyColors myTestColor;

     }

  }

  С перечислениями тоже можно работать, как с объектами. Для этого в .NET существует класс Enum (с заглавной буквы)

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

  Откроем исходный код формы и добавим внутри класса объявление перечисления MyColors, в котором будет три цвета:

  enum MyColors

  {

     Red = 100,

     Green = 200,

     Blue = 300

  };

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

foreach (string str in Enum.GetNames(typeof(MyColors)))

{

    myColorsComboBox.Items.Add(str);

}

foreach (int i in Enum.GetValues(typeof(MyColors)))

{

   indexesСomboBox.Items.Add(i);

}

  Чтобы получить массив всех имен перечисления, в первом цикле воспользуемся методом GetNames() класса Enum. В качестве параметра нужно передать тип данных для перечисления, получаемый при помощи оператора typeof. По данному типу для перечисления статичный метод GetNames() класса Enum вернет массив строк, в котором находятся имена, входящие в перечисление. В данном случае внутри цикла в выпадающий список myColorsComboBox добавляется очередное имя.

  Второй цикл тоже перебирает все элементы массива (числового), который будет возвращен статичным методом GetValues(). Значения элементов перечисления затем добавляются в выпадающий список indexesСomboBox (Индексы моих цветов)

Работа со структурами

  Объявление структуры в C# с одной стороны во многом аналогично её объявлению в языке C, с другой стороны очень похоже на описание класса. В отличие от класса, в структуре описываются только свойства. Различие со структурами С заключается в том, что перед свойствами (элементами структуры) можно указывать модификаторы доступа. Также в структуре предусмотрен конструктор, в котором можно задавать значения по умолчанию:

struct Person

{

public Person(string firstName, string lastName)

{

 FirstName = firstName;

 LastName = lastName;

   age = 18;

}

public string FirstName;

public string LastName;

public int age;

}

  При инициализации структуры, как и при создании объекта класса, использование ключевого слова new обязательно.

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

  Person p2 = new Person("Иван", "Иванов");

  Console.WriteLine("Фамилия 2: " + p2.LastName);

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

  Другой пример инициализации структуры и вывода на экран значения её элементов/свойств:

  Person p1 = new Person();

  Console.WriteLine("Фамилия 1: " + p1.LastName);

     Элемент LastName не инициализирован, поэтому фамилия на экран выведена не будет.

Перегрузка операторов

  Предположим, у нас есть класс, который представляет что-то математическое, и мы хотим работать с ним так же, как с простыми типами (int, float и т. д.), используя операторы +, -, *, / и т. д. В C# это возможно, но программисту нужно самому позаботиться о том, как будут производиться математические вычисления.

  Пусть, например, задан класс MyPoint, описывающий точку:

  class MyPoint

  {

      public MyPoint(int x, int y)

     {

         this.x = x;        /* присваивание свойству x класса Point значения,  

                                          передаваемого через соответствующий параметр

                                          конструктора */

         this.y = y;        /* присваивание свойству y класса Point значения,   

                                          передаваемого через соответствующий параметр

                                          конструктора */

 

      }

      public int x;

      public int y;

}

  Рассмотрим следующий пример работы с объектами класса Point:

  MyPoint p1 = new MyPoint(10, 20);

  MyPoint p2 = new MyPoint(20, 10);

  MyPoint p3 = p1 + p2;

  При попытке скомпилировать программу компилятор выдаст сообщение о том, что оператор + не может быть применён к операндам типа MyPoint. Для того, чтобы корректно применить операцию сложения к объектам такого типа, в классе необходимо явно реализовать нужные операторы.

  В языке C# можно перегрузить операторы +, ‒, *, /, %, &, |, ^, <<, >>, ==, !=, <, >, <= и >=.

  В рассматриваемом примере операторы + и – могут быть реализованы в классе MyPoint следующим образом:

  public static MyPoint operator + (MyPoint p1, MyPoint p2)

  {

      return new MyPoint(p1.x + p2.x, p1.y + p2.y);

  }

  public static MyPoint operator ‒ (MyPoint p1, MyPoint p2)

  {

      return new MyPoint(p1.x ‒ p2.x, p1.y ‒ p2.y);

  }

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

  Если необходимо сравнить две точки на плоскости, операторы > и < можно переопределить так:

  public static bool operator  <  (MyPoint p1, MyPoint p2)

  {

     return (p1.x + p1.y < p2.x + p2.y);

  }

  public static bool operator  >  (MyPoint p1, MyPoint p2)

  {

     return (p1.x + p1.y > p2.x + p2.y);

  }

  Операторы < и > возвращают либо значение true (истина), либо значение false (ложь). Они могут быть реализованы только одновременно, т. е., если перегружен оператор <, в том же классе должен быть переопределён и оператор >.

  Предположим, в программе нужно создавать новый объект класса точки (MyPoint) и присваивать ему координаты человека из объекта Person. Тогда преобразование объекта Person в объект MyPoint должно выглядеть так:

  MyPoint point = (MyPoint) person;

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

  public static explicit operator MyPoint(Person p)

  {

     return new MyPoint(p.x, p.y);

  }

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

Интерфейсы

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

  Объявление интерфейса начинается со слова interface. В отличие от классов, интерфейсы не наследуют никакого класса, даже автоматически наследуемого для всех классов ‒ Object. Также нельзя указать модификаторы доступа для описываемых методов, они все считаются открытыми.

  Пример интерфейса IPurse для описания кошелька:

  interface IPurse

  {

      int Sum;

      int GetSum(void);

      void AddMoney (int sum);

      int DecMoney (int sum);

  }

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

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

  В случае классом Person (со свойствами и методами аналогичными описанной выше структуре struct Person) можно реализовать интерфейс IPurse (т. е., «наделить человека кошельком») следующим образом:

  class Person: IPurse

  {

       // здесь методы класса Person

       // ...

       // Далее идет реализация интерфейса

         public int Sum;

         public int GetSum(void)

       {

            return Sum;

       }

       public void AddMoney(int sum)

      {

            Sum += sum;

            return Sum;

       }

       public int DecMoney(int sum)

       {

            Sum ‒= sum;

            return Sum;

       }

   }

   Пример применения интерфейсов на практике:

  Person person = new Person("Иван", "Иванов");

  person.AddMoney(1000000);

  В этом примере создается экземпляр класса Person c именем Иван и фамилией Иванов, после чего вызывается метод AddMoney(), чтобы положить в кошелек данного человека миллион. Несмотря на «вынужденное» (из-за реализации интерфейса IPurse) объявление, метод можно вызывать так же, как и другие методы класса.

  Имеется возможность передавать параметры типа интерфейсов другим методам. Например, если в графическом приложении сумму, которая будет сниматься или добавляться в кошелёк, вводить в поле NumericUpDown, а остаток средств в кошельке отображать в метке sumLabel, то можно разработать универсальный метод DecMoney() для такого приложения:

  void DecMoney(IPurse purse)

  {

      purse.DecMoney((int)numericUpDown1.Value);

      sumLabel.Text = purse.Sum.ToString();

  }

  В качестве параметра метод получает интерфейс. Чтобы «забрать деньги» у человека, достаточно вызвать метод следующим образом:

  DecMoney(person);

  Методы также могут возвращать значения типа интерфейсов.










Последнее изменение этой страницы: 2018-05-10; просмотров: 156.

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