Студопедия

КАТЕГОРИИ:

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

Использование класса в качестве типа данных




 

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

namespace Nasl22

{

class mas1

{ // класс mas1 будет в дальнейшем использован в качестве типа данных

   protected int[] a;

   public mas1()

   { // конструктор

       int n;

       Console.Write("Elements ");

       n = Convert.ToInt32(Console.ReadLine());

       a = new int[n];

   }

   public void inpt()

   { // ввод массива

       for (int i = 0; i < a.Length; i++)

       {

           Console.Write("a[" + i + "]=");

           a[i] = Convert.ToInt32(Console.ReadLine());

       }

   }

   int sum()

      { // нахождение суммы

       int s=0;

       for(int i=0;i<a.Length;i++)

           s+=a[i];

        return s;

   }

   public int summa

   { // свойство

       get { return sum(); }

   }

   public int this[int k]

   { // индексатор

       get { return a[k];}

       set { a[k] = value; }

   }       

}

class cl_a

{

   public mas1 arr1; //объявим переменную типа класс mas1

   int sm;

   public cl_a()

   { // конструктор класса cl_a, он создает и экземпляр mas1

       arr1 = new mas1();

       arr1.inpt();

   }

   public int st1()

   { // обращение к свойству класса mas1

       sm=arr1.summa;

       return sm;

   }

}

class Program

{

   static void Main(string[] args)

   {

       cl_a my = new cl_a();

       int n, m,r;

       n = my.arr1.summa; //обращение к свойству

       r = my.st1(); //обращение к собственной функции,

// которая в свою очередь обращается к свойству класса mas1

       m = my.arr1[2]; // работает индексатор

       Console.WriteLine("Сумма=" + n + " Сумма="+

            r+" элемент [2] =" + m);

       Console.ReadLine();

   }         }             }

 

Следующий пример иллюстрирует использование класса в качестве типа данных при работе с функциями: введем пары «имя – шифр» и выведем имя, соответствующее максимальному значению шифра (предположим, что оно единственное). Наличие обеих строк: инициализация массива и инициализация каждого элемента массива в цикле обязательно!

namespace FunKlass

{

class dan

{ // этот класс будем использовать в качестве типа данных

   public string s1;

   public int k;

}

class Program

{

  static dan[] fun2()     

  { // функция определения количества элементов dan в массиве ,

               // инициализация и ввод массива.

      int n,m;

      dan []w;

      Console.Write("Элементов ? ");

      n = Convert.ToInt32(Console.ReadLine());

      w = new dan[n]; // инициализируем массив

      for (int i = 0; i < n; i++)

      {

          w[i] = new dan(); //  инициализируем элемент массива

           Console.Write("Элемент " + i + " Имя ");

          w[i].s1 = Console.ReadLine();

          Console.Write("Элемент " + i + " Номер ");

          w[i].k=Convert.ToInt32(Console.ReadLine());

      }

      return w;

  }

  static dan fun1(dan[] x)

   {

       string t1;

       int max=0;

       dan d1=new dan();

       for (int i = 0; i < x.Length; i++)

       {

           if (max < x[i].k)

           {

               max = x[i].k;

               d1 = x[i];

           }

       }

       return d1;

   }

 

 

   static void Main(string[] args)

   {

       dan []b;

       dan otv;

       b = fun2(); //ввод массива классов

       otv = fun1(b); // обработка массива классов

       Console.WriteLine("Number= " + otv.k + " Name= " + otv.s1);

       Console.ReadLine();

   }   } }

 

Можно использовать и следующие реализации класса dan и функции fun2.

class dan

{

    public string s1;

    public int k;

   public dan()

   {

       Console.Write("Element String ");

       s1 = Console.ReadLine();

       Console.Write("Element number ");

       k = Convert.ToInt32(Console.ReadLine());

   }

}

static dan[] fun2()     

  {

      int n,m;

      dan []w;

      Console.Write("Elements ? ");

      n = Convert.ToInt32(Console.ReadLine());

     w = new dan[n];

      for (int i = 0; i < n; i++)

      { // каждый элемент вводится конструктором

          w[i] = new dan();

      }

      return w;

  }

 

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

 

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

 

namespace StructFun

{

struct dan1

{

   public string s1; // атрибут public обязателен       

   public int k;

}

class Program

{

   static dan1[] inpt()

   { // ввод массива структур

       dan1[] temp;

       int n;

       Console.Write("Elements ? ");

       n = Convert.ToInt32(Console.ReadLine());

       temp = new dan1[n];

       for (int i = 0; i < n; i++)

       {

           Console.Write("Elem " + i + " Num ");

           temp[i].k = Convert.ToInt32(Console.ReadLine());

           Console.Write("Elem " + i + " Name ");

           temp[i].s1 = Console.ReadLine();

       }

       return temp;

   }

   static double proc1(dan1 []x)

   { // обработка массива структур

       int s = 0;

       for (int i = 0; i < x.Length; i++)

           s += x[i].k;

       return (double)s / x.Length;

   }

   static void Main(string[] args)

   {

       dan1[] id;        // массив исходных данных

       double aver;

       id = inpt();  // ввод исходных данных

       aver = proc1(id); // обработка массива структур

       Console.WriteLine("Average=" + aver);

       Console.ReadLine();

   }   } }

 

Наследование

 

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

Создадим в качестве примера базовый класс для обработки массива, вклю­чающий определение массива, его ввод и вывод. К элементам базового класса с атрибутом доступа private нет доступа из классов – наследников, они, таким образом, не наследуются. Поэтому рекомендуют (если нет на этот счет особых соображений) дать элементам базового класса атрибут доступа protected.

class arr

{

   protected int[] k; //атрибут доступа protected

//необходим для обеспечения доступа из классов - наследников

   public arr()

   { // конструктор 1

       int n;

       Console.Write("Элементов ? ");

       n = Convert.ToInt32(Console.ReadLine());

       k = new int[n];

       for (int i = 0; i < n; i++)

       {

           Console.Write("K[" + i + "]=");

           k[i] = Convert.ToInt32(Console.ReadLine());

       }

   }

   public arr(int p)

   { // конструктор 2

       k = new int[p];

       for (int i = 0; i < p; i++)

       {

           Console.Write("K[" + i + "]=");               

           k[i] = Convert.ToInt32(Console.ReadLine());

       }

   }

   public void output()

   {

       Console.WriteLine();

       Console.WriteLine("Elements of Array");

        for (int i = 0; i < k.Length; i++)

           Console.WriteLine("K[" + i + "]=" + k[i]);

   } }

 

На его базе можно построить классы обработки массивов. В нашем случае – нахождение суммы. Класс-наследник включает все данные своего предка (за исключением данных с атрибутом доступа private). Наследуются по общим правилам и индексаторы и свойства, а также методы перегрузки операторов.

 

class proc1 : arr // задаем базовый класс arr

{

   int q;

   public proc1()

   { // конструктор класса наследника

       Console.Write("Граница ");

       q = Convert.ToInt32(Console.ReadLine());

   }

   public int sum()

   {

       int s = 0;

       for (int i = 0; i < k.Length; i++)

           if (k[i] > q) s += k[i];

       return s;

   }   }

Использование созданных классов

class Program

  {

   static void Main(string[] args)

   {

       proc1 My = new proc1(); //1

       int s1;

       My.output(); // обращение к методу предка

       s1=My.sum(); // обращение к собственному методу,

                    // аналогично можно обращаться и к свойствам предка

       Console.WriteLine("Summa= " + s1);

       Console.ReadLine();

   }       }

 

При создании экземпляра класса, имеющего предка, (строка // 1) запускаются все конструкторы: в первую очередь конструктор базового класса и затем конструктор класса – наследника. В нашем случае это означает, что будет осуществлен ввод сначала массива и вслед за ним – границы. При наличии большего количества уровней наследования подряд будут запущены конструкторы всех уровней иерархии, начиная с базового.

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

public proc1(int k1, int k2): base(k1)

   {

       q = k2;

   }

Запись base(k1)означает, что конструктору базового класса в качестве фактического параметра будет передано значение к1. Пример главной функции в этом случае:

 

 

static void Main(string[] args)

   {

       int s1;

       proc1 Myaa = new proc1(6, 20); //обращение к

                                                         // конструктору с параметрами

       Myaa.output();

       s1=Myaa.sum();

       Console.WriteLine("Summa= " + s1);

       Console.ReadLine();

   }

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

 

Ссылки на объекты

 

    C# является языком, требующим строгого соблюдения типа при присваивании. Автоматическое преобразование типов, применяемое при работе с обычными переменными, не распространяется на переменные ссылочного типа: ссылочная переменная одного класса не может ссылаться на объект другого класса. Исключение: ссылочной переменой базового класса можно присвоить ссылку на любой класс-наследник. Рассмотрим это на примере.

namespace Virtual1

{

class X

{

    public int a;

    public X(int i) {a=i;}

}

class Y:X

{

    public int b;

    public Y(int i, int j ): base(i) {b=j;}

}

class Class1

{       

    static void Main(string[] args)

    {

         X x1= new X(10);

         X x2;

         Y y1=new Y(15,100);

         int k;

         x2=x1; //допустимо, переменные одного типа

    Console.WriteLine("First "+x1.a+" Second "+x2.a);

         x2=y1; //допустимо, Y наследник X

    Console.WriteLine("First "+x1.a+" Second "+x2.a);

 

    // k=x2.b;ОШИБКА - класс X не имеет переменной b      Console.ReadLine(); } } }

 

Возможность доступа к членам класса зависит от типа ссылочной переменной, а не от типа объекта, на который она ссылается. Поэтому закомментированный оператор будет ошибкой. Наследуемый класс «ничего не знает» о членах класса наследника!

 

Конструктор копирования

 

    В общем случае в C# разрешено присвоение между объектами одного и то же класса. На практике это означает, что мы получим два указателя на один и тот же объект. Вспомните, точно так же было и при присвоении массивов. Для обеспечения создания нового объекта, которому в момент создания были переданы значения данных уже существующего объекта, но при этом под него выделялась собственная область памяти и в дальнейшем эти два объекта были бы полностью независимыми, необходим конструктор копирования. Единственным формальным параметром конструктора копирования всегда является переменная типа копируемый класс. При наличии конструктора копирования в классе всегда должен быть и обычный конструктор. Обратите внимание на состав формальных параметров конструктора копирования, и вы поймете, почему это так. Рассмотрим следующий пример.

namespace Construct_Coop

{

class Shape

{

   protected double a, h;

   public Shape(double x, double y)

   { // обычный конструктор

       a = x;

       h = y;

     }

   public Shape(Shape ob)

   { // конструктор копирования

       a = ob.a;

       h = ob.h;

   }

   public void NewDan(double x,double y)

   {

       a = x;

       h = y;

   }

}

   class Tri : Shape

   {

       protected double area;

       public Tri(double x, double y) : base(x, y)

       { // конструктор наследника }

       public Tri(Tri ob) :base(ob) // 1

       { // конструктор копирования наследника }

       public void Show_area()

       {

           area = a * h / 2;

           Console.WriteLine("S_Treug="+ area);

       }

   }

class Square : Shape

{

   protected double area;

   public Square(double x, double y) : base(x, y) { }

   public Square(Square ob) : base(ob) { } // 1

   public void Show_area()

   {

       area = a * h;

       Console.WriteLine("S_Squar="+ area);

   }

}

class Program

{

   static void Main(string[] args)

   {

       Tri my=new Tri(5,12); // работает конструктор

       my.Show_area();

       Tri w = my; // работает конструктор

       w.NewDan(50, 120); // новые данные для w

       my.Show_area(); // будут выведены одинаковые значения

       w.Show_area();

       Tri u = new Tri(w); //работает конструктор копирования

       u.NewDan(500, 1200); // новые данные для u

       w.Show_area();// будут выведены разные значения

       u.Show_area();

       Console.ReadLine();

   }   } }

 

Обратите внимание на строки // 1: в них имеет место присвоение указателю на базовый класс (Shape) ссылки на класс-наследник (Tri, Square).

 

Виртуальные методы

 

    Метод, при определении которого присутствует слово virtual, называется виртуальным. Каждый класс - наследник может иметь собственную версию виртуального метода, называется это переопределением и обозначается словом override. В C# выбор версии виртуального метода осуществляется в соответствии со значением указателя на момент вызова (а не типом указателя, как было в § 3.9.). Это делается во время выполнения программы. Указатель во время выполнения программы может указывать на объекты различных классов, поэтому по одному и тому же указателю могут вызываться разные версии виртуального метода. Переопределенные методы обеспечивают поддержку полиморфизма. Полиморфизм позволяет определять в базовом классе методы, которые будут общими для всех наследников, но каждый наследник, в случае необходимости, может иметь их собственные реализации. Естественно, что интерфейсы виртуального метода и всех его версий должны полностью совпадать. Таким образом, применение виртуальных методов позволяет фиксировать интерфейс метода и потом разработать под этот интерфейс новые реализации. Виртуальными могут быть и свойства и индексаторы.

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

namespace Virtual1

{

class Shape

{

protected int a,h;

    public Shape (int x,int y)

    {

    a=x;

    h=y;

    }

        publicvirtualvoid Show_area()

    { // вводится виртуальный метод

Console.WriteLine("Площадь будет определен позже");

    }

}

class Tri:Shape

{

int s;

    public Tri(int x, int y) :base(x, y)

    {}

        publicoverridevoid Show_area()

    { //первое переопределение виртуального метода

    s=a*h/2;

    Console.WriteLine("Площадь треугольника= "+s);

    }

}

class Square:Shape

{

int s;

    public Square(int x, int y):base(x, y)

    {}

        publicoverridevoid Show_area()

    { // второе переопределение виртуального метода

    s=a*h;

    Console.WriteLine("Площадь четырехугольника= "+s);

    }

}

    

class Class1

{            

    static void Main(string[] args)

    {

         Shape q=new Shape(10,30);

         q.Show_area();

         //

         Tri z1=new Tri(5,12);

         z1.Show_area();

         //

         Shape w;

         w=z1; // w будет указывать на объект класса Tri

         w.Show_area(); // Tri.Show_area()

         //

         Square w1=new Square(5,12);

         w1.Show_area();

         //

         w=w1; // w будет указывать на объект класса Square

         w.Show_area(); //Square.Show_area() 

   Console.ReadLine();  

   }          }          }

                 

Как видно из примера, указатель w имеет тип Shape, но он может указывать на все наследники Shape. Выбор версии виртуального метода зависит от значения указателя на момент вызова, поэтому вызову w.Show_area(); соответствуют разные версии Show_area().

 

 










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

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