C#. Відношення між класами типу uses (клас використовує інший клас). Приклади

Відношення між класами типу uses (клас використовує інший клас). Приклади

Дана тема є продовженням теми:


Зміст


1. Відношення між класами uses. Класифікація

При розробці проектів важливим є визначення того, як клас використовує інші класи. Різні залежності між класами з’являються неявно у мовах програмування.

Згідно з Б.Страуструпом (засновник мови C++) між двома класами A, B можна встановити наступні способи використання класом A класу B:

  • Клас A використовує ім’я класу B.
  • Клас A використовує клас B.
    • Клас A викликає функцію-член класу B.
    • Клас A читає поле даних класу B.
  • Клас A записує поле даних класу B.
  • Клас A створює об’єкт типу B.
    • Клас A виділяє пам’ять для автоматичних об’єктів типу B.
    • Клас A створює об’єкт типу B з допомогою оператора new.
  • Клас A отримує розміри класу B.

 

2. Приклад. Клас A використовує ім’я класу B

 

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Взаємодія між класами типу uses
  // Клас A використовує ім'я класу B

  // Клас B - базовий клас
  class B
  {
    // Прихована внутрішня змінна
    private int b = 25;

    // Віртуальний метод
    public virtual void Print(B refB)
    {
      WriteLine("Method B.Print():");
      WriteLine("b = {0}", refB.b);
    }
  }

  // Клас A - похідний від класу B
  class A : B
  {
    // Прихована внутрішня змінна
    private int a = 15;

    // У параметрі методу класу A використовується
    // ім'я класу B, як базового для реалізації поліморфізму
    public override void Print(B refB)
    {
      WriteLine("Method A.Print():");
      base.Print(refB);
      WriteLine("a = {0}", a);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Створити екземпляр класу A
      A objA = new A();
      B objB = new B();

      // 2. Оголосити посилання на базовий клас B
      B refB;

      // 3. Направити посилання refB на екземпляр класу A
      refB = objA;

      // 4. Викликати віртуальний метод Print() через посилання refB
      refB.Print(refB); // викликається objA.Print(refB)

      // 5. Перенаправити посилання на базовий клас B
      refB = objB;

      // 6. Викликати віртуальний метод Print() через посилання refB
      refB.Print(refB); // викликається objB.Print(refB)
    }
  }
}

Результат виконання програми

Method A.Print():
Method B.Print():
b = 25
a = 15
Method B.Print():
b = 25


 

3. Приклад. Клас A використовує клас B

У прикладі демонструється декілька форм взаємодії між класами, що відноситься до типу uses.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Взаємодія між класами типу uses
  // Клас A використовує клас B
  class B
  {
    public const double Pi2 = 6.28;
    public double Pi = 3.14;

    static public void MethodB()
    {
      WriteLine("B.MethodB()");
    }
  }

  class A
  {
    public void MethodA()
    {
      // 1. Виклик функції-члена класу B з класу A
      B.MethodB();

      // 2. Звертання до поля даних класу B з класу A
      WriteLine(B.Pi2);

      // 3. Запис поля даних класу B
      B objB = new B(); // створити екземпляр класу B
      objB.Pi = 3.141592; // записати поле даних
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Створити екземпляр класу A
      A objA = new A();

      // 2. Викликати метод MethodA() класу A
      objA.MethodA();
    }
  }
}

Результат роботи програми

B.MethodB()
6.28

 

4. Приклад. Клас A динамічно створює об’єкт класу B

Даний приклад більш актуальний для мови C++, оскільки у цій мові об’єкт класу в методі можна створити двома способами:

// Мова C++. Спосіб 1
B objB;

// Мова C++. Спосіб 2 - з допомогою оператора new
B* pB = new B();

У мові C# усі об’єкти (екземпляри) класу створюються динамічно з допомогою оператора new. Це пов’язано з тим, що класи відносяться до типів-посилання а не до типів-значення. У випадку з класами типи-посилання містять посилання на екземпляр класу. Саме посилання розміщується в стеку, а екземпляр розміщується в кучі (heap). Тому, для розміщення екземпляру потрібно виділити пам’ять динамічно.

Текст демонстраційної програми наступний.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Взаємодія між класами типу uses
  // Клас A динамічно створює об'єкт класу B
  class B
  {
    // Внутрішнє поле
    public int d;

    // Метод Show()
    public void Show()
    {
      WriteLine("d = {0}", d);
    }
  }

  class A
  {
    // Метод, що динамічно створює об'єкт класу B
    public void Demo()
    {
      WriteLine("Method A.Demo()");

      // 1. Створити екземпляр класу B динамічно
      WriteLine("Creating instance objB.");
      B objB = new B();
      WriteLine("OK!");

      // 2. Заповнити значенням об'єкт класу B
      WriteLine("Fill with values objB.d.");
      objB.d = 330;
      WriteLine("OK!");
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Створити екземпляр класу A
      A objA = new A();

      // 2. Викликати метод Demo() класу A
      objA.Demo();
    }
  }
}

Результат роботи програми

Method A.Demo()
Creating instance objB.
OK!
Fill with values objB.d.
OK!

 

5. Приклад. Клас A отримує розміри класу B

У прикладі демонструється визначення розмірів екземпляру класу з допомогою механізму серіалізації.

using System;
using static System.Console;

// Необхідно для використання можливостей класу Stream
using System.IO;

// Необхідно для використання класу BinaryFormatter
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApp1
{
  // Вихідний клас B, який містить одне поле та один метод
  // Перед класом вказується, що клас буде зберігатись в пам'яті
  // (клас, який серіалізується)
  [Serializable]
  class B
  {
    public double x;

    void ShowX()
    {
      WriteLine("x = {0}", x);
    }
  }

  // Клас A, який містить метод,
  // що визначає розмір екземпляру класу B
  class A
  {
    // У методі створюється екземпляр класу B,
    // та визначається його розмір
    public void DemoSizeObjB()
    {
      // 1. Оголосити внутрішні змінні
      long size = 0; // розмір екземпляру класу B
      B objB = new B(); // створити екземпляр

      // 2. Використання потоку в пам'яті у двійковому вигляді
      // 2.1. Створити потік в пам'яті
      using (Stream s = new MemoryStream())
      {
        // 2.2. Створити екземпляр бінарного (двійкового)
        //      формату серіалізації
        BinaryFormatter bf = new BinaryFormatter();

        // 2.3. Записати екземпляр objB у двійковому форматі
        //      в пам'ять
        bf.Serialize(s, objB);

        // 2.4. Визначити розмір пам'яті,
        //       в який був записаний екземпляр
        size = s.Length;

        // 2.5. Вивести розмір на екран
        WriteLine("size = {0}", size);
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Створити екземпляр класу A
      A objA = new A();

      // 2. Викликати метод DemoSizeObjB()
      objA.DemoSizeObjB();
    }
  }
}

Результат роботи програми

size = 129

 


Зв’язані теми