Кратко съдържание


Оператори за работа с типове в C#



страница42/73
Дата21.07.2018
Размер9.03 Mb.
#76887
1   ...   38   39   40   41   42   43   44   45   ...   73

Оператори за работа с типове в C#


В C# има няколко служебни оператора за работа с типове – is и as и typeof.

Оператор is


Операторът is проверява дали зададеният обект е инстанция на даден тип. Пример:

int value = 5;

if (value is System.Object) // it is true

{

Console.WriteLine("{0} is instance of System.Object.", value);



}

Оператор as


Операторът as преобразува даден референтен тип в друг, като при неуспех не предизвиква изключение, а връща стойност null. При стан­дартно преобразуване на типове, ако има несъвместимост на обекта с резултатния тип, се получава изключение. Например:

int i = 5;

object obj = i;

string str = (string) obj; // System.InvalidCastException


Операторът as преобразува типове, без да предизвиква изключение:

int i = 5;

object obj = i;

string str = obj as string; // str == null

Оператор typeof


Операторът typeof извлича отражението на даден тип във вид на инстан­ция на System.Type. Пример:

Type intType = typeof(int);

В темата "Отражение на типовете" ще обърнем повече внимание на типа System.Type и на оператора typeof.

Оператори is и as – пример


В следващия пример се илюстрира използването на операторите is и as:

using System;
class Base

{

}


class Derived : Base

{

}


class TestOperatorsIsAndAs

{

static void Main()



{

Object objBase = new Base();

if (objBase is Base)

{

Console.WriteLine("objBase is Base");



}

// Result: objBase is Base


if (! (objBase is Derived))

{

Console.WriteLine("objBase is not Derived");



}

// Result: objBase is not Derived


if (objBase is System.Object)

{

Console.WriteLine("objBase is System.Object");



}

// Result: objBase is System.Object

Base b = objBase as Base;

Console.WriteLine("b = {0}", b);

// Result: b = Base
Derived d = objBase as Derived;

if (d == null)

{

Console.WriteLine("d is null");



}

// Result: d is null


Object o = objBase as Object;

Console.WriteLine("o = {0}", o);

// Result: o = Base
Derived der = new Derived();

Base bas = der as Base;

Console.WriteLine("bas = {0}", bas);

// Result: bas = Derived

}

}


Примерът декларира два класа – Base и негов наследник Derived, след което създава няколко инстанции от тези класове и ги преобразува една към друга. Работата на операторите is и as се илюстрира чрез няколко преобразувания и проверки на типовете. Резултатът от изпълнението на примерната програма е следния:


Клониране на обекти


Клонирането (копирането) на обекти е операция, която създава иден­тично копие на даден обект. При клонирането се създават копия на всички информационни полета (член-променливи) на типа. Съществуват 2 типа клониране – плитко и дълбоко.

Плитко клониране


При плитко клониране всички стойностни типове се копират, а всички референции се дублицират (копират се адресите). На практика се създава копие на обекта, което може да има общи (споделени) части с оригинал­ния обект (това са всички полета на оригиналния обект, които са от рефе­рентен тип). Плитко клониране се извършва от метода MemberwiseClone() на типа System.Object.

Дълбоко клониране


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

Плитки срещу дълбоки копия


В програмирането използването на плитки копия на обектите често води до проблеми и затова не е препоръчвана практика. Когато трябва да се клонира даден обект, обикновено е необходимо да се създаде негово пълно копие, а не само нова референция, сочеща към оригиналния обект.

В някои редки случаи, от съображения за производителност и пестене на ресурси, се налага да се ползват плитки или частични копия на обектите. Ако се прилагат такива техники, това трябва да се прави много внима­телно, за да не се получават странни проблеми, като синдромът "ама това вчера работеше".


Интерфейсът ICloneable


В .NET Framework под клониране се подразбира "дълбоко клониране". Всички типове, които позволяват клониране, трябва да имплементират интерфейса System.ICloneable.

ICloneable дефинира метод Clone() който връща идентично копие на обекта. Clone() методът трябва да връща дълбоко копие на оригиналния обект. Ако даден обект съдържа като член-данни други обекти, тези обекти трябва също да имплементират ICloneable и да бъдат клонирани посредством Clone() метода им. Ако това не бъде изпълнено, има веро­ятност клонирането да не работи правилно и да се получат споделени данни между оригиналния обект и копието.

Клониране на обекти в .NET Framework


Клонирането като цяло е проблем, при който често възникват грешки, но за щастие рядко се налага да бъде имплементирано ръчно.

Голяма част от често използваните стандартни типове в .NET Framework имат имплементация на ICloneable – масивите, колекциите, символните низове и др. Примитивните стойностни типове (int, float, double, byte, char и т. н.) могат да бъдат клонирани чрез просто присвояване, защото не съдържат вложени членове от референтен тип. При тях на практика всяко клониране е дълбоко.


Имплементиране на ICloneable – пример


В следващия пример ще илюстрираме как може да се клонира нетриви­ална структура от данни, а именно динамично реализиран свързан списък. При него всеки елемент съдържа някаква стойност и референция към следващ елемент. Последният елемент съдържа за следващ елемент стой­ност null.

При клонирането на свързан списък трябва да се клонират всичките му елементи и връзките между тях. В резултат трябва да се построи нов списък, който съдържа елементите от първия в реда, в който са били в него. На практика клонирането на свързан списък се свежда до обхож­дането му и построяването на копие на всеки негов елемент и на всяка връзка между два елемента. Следва примерна реализация:



using System;

using System.Text;


class LinkedList : ICloneable

{

public string mValue;



protected LinkedList mNextNode;
public LinkedList(string aValue, LinkedList aNextNode)

{

mValue = aValue;



mNextNode = aNextNode;

}
public LinkedList(string aValue) : this(aValue, null)

{

}
// Explicit implementation of ICloneable.Clone()



object ICloneable.Clone()

{

return this.Clone();



}
// This method is not ICloneable.Clone()

public LinkedList Clone()

{

// Clone the first element



LinkedList original = this;

string value = original.mValue;

LinkedList result = new LinkedList(value);

LinkedList copy = result;

original = original.mNextNode;
// Clone the rest of the list

while (original != null)

{

value = original.mValue;



copy.mNextNode = new LinkedList(value);

original = original.mNextNode;

copy = copy.mNextNode;

}
return result;

}
public override string ToString()

{

LinkedList currentNode = this;



StringBuilder sb = new StringBuilder("(");

while (currentNode != null)

{

sb.Append(currentNode.mValue);



currentNode = currentNode.mNextNode;

if (currentNode != null)

{

sb.Append(", ");



}

}

sb.Append(")");


return sb.ToString();

}

}


class TestClone

{

static void Main()



{

LinkedList list1 =

new LinkedList("Бай Иван",

new LinkedList("Баба Яга",

new LinkedList("Цар Киро")));
Console.WriteLine("list1 = {0}", list1);

// Result: list1 = (Бай Иван, Баба Яга, Цар Киро)


LinkedList list2 = list1.Clone();

list2.mValue = "1st changed";


Console.WriteLine("list2 = {0}", list2);

// Result: list2 = (1st changed, Баба Яга, Цар Киро)


Console.WriteLine("list1 = {0}", list1);

// Result: list1 = (Бай Иван, Баба Яга, Цар Киро)

}

}


В примерната реализация е дефиниран свързан списък от символни низове. Методът ICloneable.Clone() е реализиран експлицитно (явно). Допълнително за удобство е дефиниран метод Clone(). Разликата между двата метода е във връщания тип. Имплементацията на интерфейса ICloneable (методът ICloneable.Clone()) връща object и ако се използва, трябва да се извършва преобразуване. Методът Clone() връща директно правилния тип и ни спестява преобразуването.

Методът ToString() използва специалния клас StringBuilder за по-ефективно сглобяване на резултатния низ. Класът StringBuilder и при­чи­ните за използването му ще бъдат разгледани подробно в темата за работа със символни низове.

Главната програма създава списък list1, съдържащ 3 елемента, и го отпечатва. След това го клонира в променливата list2 и променя първия му елемент. Тъй като оригиналният списък и неговото копие не съдържат споделени данни, оригиналният списък не се променя и това ясно личи от изведения резултат:





Сподели с приятели:
1   ...   38   39   40   41   42   43   44   45   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница