Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Механизмы управления памятью в C#.




Оперативная память компьютера, в которой можно выделять память для хранения объектов, называют динамически распределяемой областью памяти или кучей. В MS.NET Framework имеется специальный компонент Garbage Collector (сборщик мусора), который отвечает за механизмы выделения и освобождения памяти в куче.

Механизм выделения памяти в куче следующий:

· Программе в момент ее запуска системой выделяется некоторая область памяти (куча). На начало непрерывного свободного участка кучи указывает системная ссылка (ссылка свободной памяти – ССП).

· В момент, когда программа запрашивает память из кучи, используя оператор new, Garbage Collector проверяет, достаточно ли памяти в куче (объем свободной памяти определяется количеством байтов между ССП и концом кучи). Если памяти достаточно, то объект располагается в памяти, начиная с адреса, на который указывает ССП, а сама ссылка перемещается на новое начало свободной памяти.

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

Таким образом, выделение памяти происходит последовательно.

С целью повторного использования освобождаемой памяти Garbage Collector периодически производит дефрагментацию кучи («сборку мусора»). Этот процесс состоит из нескольких этапов.

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

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

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

Основной недостаток данного механизма освобождения памяти состоит в том, что в момент сборки мусора может понадобиться копировать большие объемы памяти. Поэтому в.NET Framework имеется две кучи, одна из которых предназначена для хранения небольших объектов и управляется с помощью механизма сборки мусора, а вторая предназначена специально для больших объектов. В механизме управления второй кучей дефрагментируется только часть кучи, что существенно ускоряет работу Garbage Collector.

При управлении кучей для больших объектов в MS.NET используется специально- разработанный алгоритм для определения возможного времени существования объектов. Механизм поколений позволяет оптимизировать сбор мусора, т.к. Garbage Collector проводит его именно тогда, когда это необходимо, просматривая при этом только часть кучи.

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

Для явного освобождения памяти используется интерфейс IDisposable.

Программно взаимодействовать со сборщиком мусора предоставляет возможность класс System.GC.

 

Наследование в C#.

Одним из основных принципов ООП является наследование – это процесс, в ходе которого один объект приобретает свойства другого объекта. В языке C# класс, который наследуется, называется базовым, а класс, который наследует, – производным. Производный класс наследует все доступные методы, свойства и индексаторы, определяемые в базовом классе, добавляя к ним свои собственные элементы.

Для наследования в C# необходимо при объявлении производного класса после его имени через двоеточие указать имя базового класса.

Базовый класс является совершенно независимым и самодостаточным классом.

Для любого производного класса можно указать только один базовый класс.

Наследование класса не отменяет ограничения, накладываемые на доступ к закрытым членам класса.

Модификатор доступа protected позволяет делать защищенные члены класса доступными и для производных классов при наследовании.

С помощью формы расширенного объявления конструктора производного класса и ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе:

конструктор_производного_класса (список_параметров): base (список_аргументов) {//тело конструктора }

где список_аргументов – аргументы, необходимые конструктору в базовом классе.

В иерархии классов конструкторы вызываются по порядку выведения классов: от базового к производному. Этот порядок остается неизменным независимо от использования ключевого слова base. Если слово base не используется, то выполняется конструктор по умолчанию (без параметров).

В производном классе можно определить член с таким же именем, как и у члена его базового класса. В этом случае член базового класса скрывается в производном. Если член базового класса необходимо скрыть намеренно, то перед его именем необходимо указать ключевое слово new в производном классе. Для доступа к скрытому имени члена базового класса служит ключевое слово base.

Пример наследования:

using System;

namespace Example

{

class Fruit

{

public double Weight;

public string Color;

public string Ripeness;

public void Weight_out()

{

Console.WriteLine("Вес равен " + Weight + " кг.");

}

}

class Apple: Fruit

{

public double Diameter;

public int Age_apple_tree;

}

class Antonovka: Apple

{

public string Kislost;

public Antonovka()

{

Weight = 0.2;

Color = "Зеленый";

Kislost = "Очень кислое";

}

}

class ExampleDemo

{

static void Main()

{

Antonovka ant = new Antonovka();

ant.Weight_out();

}

}

}

Для того чтобы предотвратить наследование класса, необходимо указать ключевое слово sealed перед определением класса:

sealed class имя_класса {}

 

22. Классы, закрытые для наследования в C#

Для того чтобы предотвратить наследование класса, необходимо указать ключевое слово sealed перед определением класса:

sealed class имя_класса {}

Не допускается одновременно объявлять класс как sealed, abstract и static, он может иметь только один из этих модификаторов или не иметь вообще.

Пример

sealed class Example

{

public int x;

public string Text;

bool flag;

public Example(int i)

{

x+=i;

Text = “Test”;

}

~Example(int k)

{

x-=k;

}

public void Text_out()

{

Console.WriteLine(Text);

}

public int Sum(int z)

{

return (z+x);

}

}

 

 

Модификатор new в C#.

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

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

Например:

public class BaseC

{

public int x;

public void Invoke() { }

}

public class DerivedC: BaseC

{

new public void Invoke() { }

}

Скрытие имени через наследование принимает одну из следующих форм.

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

· Метод, представленный в классе или структуре, скрывает в базовом классе свойства, поля и типы с одним именем. Он также скрывает все методы базовых классов, имеющие одинаковую сигнатуру.

· Индексатор, представленный в классе или структуре, скрывает все индексаторы базового класса, имеющие одинаковую сигнатуру.

Совместное использование модификаторов new и override в одном члене является недопустимым, поскольку два модификатора имеют взаимоисключающие значения. Модификатор new создает новый член с таким же именем и приводит к скрытию исходного члена. Модификатор override расширяет реализацию для наследуемого члена.

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

 

 

24. Полиморфизм в C#

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

К одному из способов реализации полиморфизма в C# относится перегрузка методов. Перегрузка методов – это процесс совместного использования одного и того же имени двумя или более методами одного и того же класса, при условии, что их параметры объявляются по-разному. Главная ценность перегрузки заключается в том, что она обеспечивает доступ к связанным вместе методам по общему имени. Конструкторы также могут быть перегружены.

Пример перегрузки методов:

using System;

namespace Example

{

class Example

{

public int Sum (int x)

{

return ++x;

}

public int Sum (int x, int y)

{

return x + y;

}

 

}

class ExampleDemo

{

static void Main()

{

Example ex = new Example();

Console.WriteLine(ex.Sum(10));

Console.WriteLine(ex.Sum(5, 10));

}

}

}

Динамическая диспетчеризация методов – механизм разрешения вызова во время выполнения, а не компиляции. Благодаря ей в C# реализуется динамический полиморфизм. Основанием для динамической диспетчеризации служит переопределение метода – процесс повторного определения виртуального метода в производном классе. Виртуальный метод объявляется как virtual в базовом классе. Вариант выполнения виртуального метода выбирается по типу объекта, и происходит это во время выполнения. Когда виртуальный метод переопределяется в производном классе, необходимо использовать модификатор override и его имя, возвращаемый тип и сигнатура должны быть такими же, как и в базовом классе. Виртуальный метод не может быть объявлен как static или abstract.

Пример переопределения метода:

using System;

namespace Example

{

class Base

{

public virtual void ShowText()

{

Console.WriteLine("Класс Base");

}

}

class Child: Base

{

public override void ShowText()

{

Console.WriteLine("Класс Child");

}

}

class ExampleDemo

{

static void Main()

{

Base b = new Base();

Child ch = new Child();

 

Base baseRef;

baseRef = b;

baseRef.ShowText();

baseRef = ch;

baseRef.ShowText();

}

}

}

Для реализации полиморфизма также можно использовать абстрактный класс – это класс, в котором определена лишь общая форма для всех его производных классов, а наполнение ее деталями предоставляется каждому из этих классов. У абстрактного метода отсутствует тело, он реализуется только в производных классах, а объявляется с помощью ключевого слова abstract. Класс, содержащий один или более абстрактных методов, также должен быть объявлен как абстрактный. У абстрактного класса не может быть объектов.

Пример применения абстрактного класса:

 

using System;

namespace Example

{

abstract class Base

{

public int i;

public int Sum(int x)

{

return x + i;

}

public abstract void ShowText();

}

class Child1: Base

{

public override void ShowText()

{

Console.WriteLine("Класс Child1");

}

}

class Child2: Base

{

public override void ShowText()

{

Console.WriteLine("Класс Child2");

}

}

class ExampleDemo

{

static void Main()

{

Child1 ch1 = new Child1();

Child2 ch2 = new Child2();

ch1.ShowText();

ch2.ShowText();

}

}

}

Благодаря поддержке интерфейсов в C# может быть реализован полиморфизм. Интерфейс определяет ряд методов для реализации в классе. Интерфейсы определяются с помощью ключевого слова interface и они подобны абстрактным классам, но у методов интерфейса нет тела.

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

using System;

namespace Example

{

interface IExample

{ void ShowText(); }

class A: IExample

{

public void ShowText()

{

Console.WriteLine("Класс A");

}

}

class B: IExample

{

public void ShowText()

{

Console.WriteLine("Класс B");

} }

class ExampleDemo

{ static void Main()

{

A a = new A();

B b = new B();

a.ShowText();

b.ShowText();

}

}

}

25. Виртуальные и невиртуальные методы в C#

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

Форма определения невиртуального метода:

доступ возвращаемый_тип имя (список_параметров)

{//тело метода}

где доступ – модификатор доступа, возвращаемый_тип – тип данных, возвращаемых методом (если метод не возвращает данных, то указывается void), список_параметров – последовательность пар, состоящих из типа и идентификатора и разделенных запятыми (переменные, получающие значения аргументов, передаваемых методу при его вызове).

Виртуальным называется такой метод, который объявляется с помощью модификатора virtual в базовом классе. Если модификатора virtual нет, то такой метод называют невиртуальным. Реализация невиртуального метода одна. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Переопределение метода – процесс замены описания унаследованного от базового класса метода.

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

Для переопределение метода в производном классе при его объявлении вместо ключевого слова virtual необходимо указать override.

Пример:

using System;

namespace Example

{

class Base

{

public void Method1()

{

Console.WriteLine("Невиртуальный метод в базовом классе");

}

public virtual void Method2()

{

Console.WriteLine("Виртуальный метод в базовом классе");

}

}

class Child: Base

{

new public void Method1()

{

Console.WriteLine("Невиртуальный метод в производном классе");

}

public override void Method2()

{

Console.WriteLine("Виртуальный метод в производном классе");

}

}

class ExampleDemo

{

static void Main()

{

Base b = new Base();

Child ch = new Child();

b.Method1();

ch.Method1();

b.Method2();

ch.Method2();

}

}

}

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...