<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Клуб программистов</title>
	<atom:link href="http://progclub.ru/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://progclub.ru</link>
	<description>Лучшая подборка книг и информации по программированию.</description>
	<lastBuildDate>Mon, 05 Jul 2010 09:45:36 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Пример BubbleSorter.</title>
		<link>http://progclub.ru/?p=2668</link>
		<comments>http://progclub.ru/?p=2668#comments</comments>
		<pubDate>Fri, 02 Jul 2010 18:26:40 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2668</guid>
		<description><![CDATA[Теперь мы приведем пример, в котором делегаты чрезвычайно полезны. Напишем класс BubbleSorter. Он реализует статический метод Sort(), который в качестве первого параметра принимает массив объектов и сортирует его в возрастающем порядке. Например, если мы передадим массив int {0. 5, 6, 2,1}, метод отсортирует его так: {0,1. 2, 5, 6}.
Алгоритм пузырьковой сортировки является довольно известным и [...]]]></description>
			<content:encoded><![CDATA[<p>Теперь мы приведем пример, в котором делегаты чрезвычайно полезны. Напишем класс BubbleSorter. Он реализует статический метод Sort(), который в качестве первого параметра принимает массив объектов и сортирует его в возрастающем порядке. Например, если мы передадим массив int {0. 5, 6, 2,1}, метод отсортирует его так: {0,1. 2, 5, 6}.<br />
Алгоритм пузырьковой сортировки является довольно известным и очень простым (хотя неэффективным) способом сортировки чисел. Он работает, проходя по всему массиву, сравнивая каждую пару чисел и меняя их в случае необходимости, в результате чего большие числа постепенно перемещаются в конец массива. Для сортировки int метод пузырьковой сортировки выглядит так:<br />
<span id="more-2668"></span></p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
// Это не часть примера<br />
for (int i=0; i<sortArray.Length; i++){<br />
	for (int j=0; j<i; j++){<br />
		if (j > i) // проблема с этой проверкой<br />
		{<br />
			int temp = sortArray [i]; // меняем i и j<br />
			sort Array[i] = sortArray[j];<br />
			sortArray[j] = temp;<br />
		}<br />
	}<br />
}</div>
<p>Все хорошо для чисел int, но требуется, чтобы метод Sort() мог сортировать любые объекты. Другими словами, если клиентский код передает массив структур Currency или любых других классов и структур, которые он смог определить, мы должны суметь отсортировать его. Это приводит к появлению проблемы в строке if (j>i) кода, так как она требует сравнения двух объектов массива для оппеделения того, какой из них больше. Мы можем выполнить это сравнение для чисел int, но как мы сделаем это для класса, который ранее нам не встречался? Ответ заключается в том, что клиентский код, которому известно о существовании этого класса, передаст в наш код делегата, который оборачивает метод, выполняющий сравнение.<br />
Определим этого делегата как:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
delegate bool CompareOp(object lhs, object rhs);</div>
<p>А метод Sort() будет иметь сигнатуру:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
static public void Sort(object [] sortArray, CompareOp rhsIsGreater)</div>
<p>В документации для этого метода будет отмечено, что rhsIsGreater должен ссылаться на статический метод, принимающий два аргумента и возвращающий true в том случае, если значение второго аргумента &#8220;больше&#8221; (т.е. он должен следовать в массиве позже), чем значение первого.<br />
Теперь определим класс BubbleSorter:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
class BubbleSorter{<br />
	static public void Sort(object [] sortArray, CompareOp rhsIsGreater){<br />
		for (int i=0; i<sortArray.Length; i++){<br />
			for (int j=0; j<i; j++){<br />
				if (rhsIsGreater(sortArray[i], sortArray[j]){<br />
					object temp = sortArray[i];<br />
					sortArray[i] = sortArray[j];<br />
					sortArray[j] = temp;<br />
				}<br />
			}<br />
		}<br />
	}<br />
}</div>
<p>Для того чтобы использовать этот класс, необходимо определить другой класс, в котором можно создать массив, требующий сортировки. Предположим, что наша компания сотовой связи Mortimer Phones имеет список работников и желает отсортировать его по их зарплатам. Каждый из работников представлен экземпляром класса Employee, который выглядит следующим образом:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
class Employee{<br />
	private string name;<br />
	private decimal salary;<br />
	public Employee(string name, decimal salary){<br />
		this.name = name;<br />
		this.salary = salary;<br />
	}<br />
	public override string ToString(){<br />
		return string.Format(name + &#8220;, {0:C}&#8221;, salary);<br />
	}<br />
	public static bool RhsIsGreater (object lhs, object rhs){<br />
		Employee Lhs = (Employee)lhs;<br />
		Employee Rhs = (Employee)rhs;<br />
		return (Rhs.Salary > Lhs.Salary) ? true : false;<br />
	}<br />
}</div>
<p>Для того чтобы добиться соответствия сигнатуре делегата CompareOp, нам пришлось определить RhsisGreater в этом классе как принимающий в качестве параметров две ссылки на object, а не на Employee. Это означает, что для выполнения сравнения мы должны привести параметры к ссылкам Employee.<br />
Теперь можно написать клиентский код, который будет выполнять сортировку.</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
using System;<br />
namespace BubbleSorrer{<br />
	delegate bool CompareOp(object lhs, object rhs);<br />
	class Class1{<br />
		static void Main(string[] args){<br />
			Employee [] employees =<br />
			{<br />
				new Employee(&#8221;Karli Watson&#8221;, 20000),<br />
				new Employee(&#8221;Bill Gates&#8221;, 10000),<br />
				new Employee(&#8221;Simon Robinson&#8221;, 25000),<br />
				new Employee(&#8221;Mortimer&#8221;, (decimal)1000000.38),<br />
				new Employee(&#8221;Arabel Jones&#8221;, 23000),<br />
				new Employee(&#8221;Avon from &#8216;Blake&#8217;s 7&#8242;&#8221;, 50000)<br />
			};<br />
			CompareOp EmployeeCompareOp = new CompareOp(Employee.RhsisGreater);<br />
			BubbleSorter.Sort(employees, EmployeeCompareOp);<br />
			for (int I=0; I<employees.Length; I++)<br />
				Console.WriteLine (employees [I] .ToString()) ;<br />
		}<br />
	}<br />
}</div>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2668</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Простой пример использования делегатов.</title>
		<link>http://progclub.ru/?p=2665</link>
		<comments>http://progclub.ru/?p=2665#comments</comments>
		<pubDate>Fri, 02 Jul 2010 18:11:31 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2665</guid>
		<description><![CDATA[Определим класс MathOperations, который имеет пару статических методов для выполнения действий над числами double. Затем используем делегатов для вызова этих методов. Математический класс выглядит следующим образом:

class MathOperations{
	public static double MultiplyByTwo(double value){
		return value*2;
	}
	public static double Square(double value){
		return value*value;
	}
}

Вызываются эти методы так:

using System;
namespace Wrox.ProfessionalCSharp.Chepter6.SimpleDelegate {
	delegate double BoubleOp (double x);
	class MainEntryPoint{
		static void Main(scring[] args){
			DoubleOp [] operations =
			{
				new DoubleOp(MathOperations.MultiplyByTwo), [...]]]></description>
			<content:encoded><![CDATA[<p>Определим класс MathOperations, который имеет пару статических методов для выполнения действий над числами double. Затем используем делегатов для вызова этих методов. Математический класс выглядит следующим образом:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
class MathOperations{<br />
	public static double MultiplyByTwo(double value){<br />
		return value*2;<br />
	}<br />
	public static double Square(double value){<br />
		return value*value;<br />
	}<br />
}</div>
<p><span id="more-2665"></span><br />
Вызываются эти методы так:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
using System;<br />
namespace Wrox.ProfessionalCSharp.Chepter6.SimpleDelegate {<br />
	delegate double BoubleOp (double x);<br />
	class MainEntryPoint{<br />
		static void Main(scring[] args){<br />
			DoubleOp [] operations =<br />
			{<br />
				new DoubleOp(MathOperations.MultiplyByTwo), new DoubleOp(MathOperations.Square)<br />
			};</p>
<p>			for (int i=0; i < operations.Length; i++){<br />
				Console.WriteLine("Using operations[{0}]:", i);<br />
				ProcessAndDisplayNumber(operations[i], 2.0);<br />
				ProcessAndDisplayNumber(operations[i], 7.94);<br />
				ProcessAndDisplayNumber(operations[i], 1.414);<br />
				Console.WriteLine();<br />
			}<br />
			static void ProcessAndDisplayNumber(DoubleOp action, double value){<br />
				double result = action(value);<br />
				Console.WriteLine("Value is {0}, result of operation is {1}", value, result);<br />
			}<br />
		}<br />
	}<br />
}</div>
<p>В этом коде мы создаем массив делегатов DoubleOp. (Напомним, что, определив класс делегата, мы можем создавать его экземпляры точно так же. как и для обычных классов, поэтому их помещение в массив не является проблемой.) Каждый элемент массива инициализируется ссылкой на отдельные операции, реализованные в классе MathOperations. Затем мы в цикле проходим по массиву, применяя каждую операцию к трем отдельным значениям. Этим мы показываем один из способов применения делегатов: их можно группировать в массив, а несколько методов можно вызывать в цикле.<br />
Ключевыми в этом коде являются строки, где мы передаем каждого делегата в метод ProcessAndDispiayNumber(). например:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
ProcessAndDispiayNumber(operations[i], 2.0) ;</div>
<p>Здесь передается имя делегата, но без параметров. При условии, что operations[I] является делегатом, синтаксически:<br />
&#8220;operations[i] &#8221; означает &#8220;делегат&#8221;, т.е. метод, представляемый делегатом &#8220;operations[i] (2.0) &#8221; означает &#8220;вызвать этот метод&#8221;<br />
Метод ProcessAndDispiayNumber() принимает делегата в качестве своего первого параметра:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
static void ProcessAndDisplayNuinbev(DoubleOp action, double value)</div>
<p>Затем, находясь внутри этого метода, мы вызываем:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
double Result = action(value);</div>
<p>Это приводит к вызову метода, обернутого экземпляром делегата action, а возвращаемый результат сохраняется в переменной Result.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2665</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Использование делегатов в С#.</title>
		<link>http://progclub.ru/?p=2663</link>
		<comments>http://progclub.ru/?p=2663#comments</comments>
		<pubDate>Mon, 10 May 2010 19:00:12 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2663</guid>
		<description><![CDATA[Для того чтобы можно было использовать класс в С#, нужно выполнить два действия. Во-первых, необходимо определить класс, т.е. сказать компилятору, какие поля и методы образуют класс. Затем (если только мы не используем статические методы) необходимо создать экземпляр класса, т.е. создать объект этого класса. То же самое справедливо для делегатов. Сначала требуется определить делегатов, которые будут [...]]]></description>
			<content:encoded><![CDATA[<p>Для того чтобы можно было использовать класс в С#, нужно выполнить два действия. Во-первых, необходимо определить класс, т.е. сказать компилятору, какие поля и методы образуют класс. Затем (если только мы не используем статические методы) необходимо создать экземпляр класса, т.е. создать объект этого класса. То же самое справедливо для делегатов. Сначала требуется определить делегатов, которые будут применяться. Определение делегата означает указание компилятору того, какой тип методов будут представлять делегаты этого типа. Затем нужно создать один или несколько экземпляров делегатов.<br />
Синтаксис для определения делегатов выглядит следующим образом: delegate void VoidOperation(uint X);<br />
В данном случае мы определили делегата VoidOperation и указали, что каждый экземпляр этого делегата может содержать сведения о методе, принимающем один параметр uint и возвращающем void. Ключевым моментом является то, что делегаты безопасны по типу. При определении делегата вы должны указать все подробности сигнатуры метода, который он будет представлять.<br />
<span id="more-2663"></span><br />
<center>
<div class="table_black">Удобно рассматривать делегата как способ именования сигнатуры метода.</div>
<p></center><br />
Допустим, что мы хотим определить делегата TwoLongsOp, который будет представлять функцию, принимающую в качестве параметров два значения long и возвращающую double. Мы могли бы сделать это следующим образом:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
delegate double TwoLongOp(long LI, long L2);</div>
<p>Делегата, который представляет собой метод без параметров, возвращающий строку, можно определить так:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
delegate string GetAString();</div>
<p>Синтаксис аналогичен tomv, который используется при определении методов, за исключением того, что отсутствует тело метода, а определение предваряется ключевым словом delegate. Поскольку то, что мы делаем, представляет собой по сути определение класса, делегата можно определять там же, где и класс, т.е. либо внутри другого класса, либо вне всякого класса в пространстве имен как объект наивысшего уровня. В зависимости от того, насколько доступным должно быть определение делегата, можно примените один из обычных модификаторов доступа — public, private, protected и т.п.:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public delegate string GetAString();</div>
<p><i>Когда мы говорим &#8220;определение делегата&#8221;, это буквально означает &#8220;определение нового класса&#8221;. Делегаты реализуются как экземпляры классов, производных от базового класса System.Delegate. Компилятору С# известно об этом классе, и он использует его синтаксис для делегатов, чтобы скрыть от нас детали его работы. Это еще один хороший пример того, как С# работает совместно с базовыми классами для того, чтобы сделать программирование настолько простым, насколько это возможно.</i><br />
Определив делегата, мы можем создать его экземпляр и использовать его для хранения сведений о конкретном методе.<br />
<i>К сожалению, здесь имеется проблема с терминологией. Для классов существуют два отдельных термина: &#8220;класс&#8221;, который означает определение, и &#8220;объект&#8221;, который означает экземпляр класса. К сожалению, для делегатов используется только один термин. То, что получается при создании экземпляра делегата, также называется делегатом. Поэтому когда мы говорим о делегатах, необходимо по контексту определять, о чем конкретно идет речь.</i><br />
Следующий фрагмент кода демонстрирует использование делегатов. Это довольно долгий путь вызова метода ToString() для int:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
private delegate string GetAString();<br />
static void Main(string[] args){<br />
	int X = 40;<br />
	GetAString FirstStringMethod = new GetAString(X.ToString);<br />
	Console.WriteLine(&#8221;String is &#8221; + FirstStringMethod());<br />
	// С FirstStringMethod инициализированным X.ToString()<br />
	// приведенный выше оператор эквивалентен<br />
	// Console.WriteLine(&#8221;String is &#8221; + X.ToString());</div>
<p>В этом коде мы создали экземпляр делегата типа GetAString и инициализировали его таким образом, что он стал ссылаться на метод ToString() целочисленной переменной X. Делегаты в С# всегда синтаксически принимают конструктор с одним параметром, в качестве параметра передается метод, на который будет ссылаться делегат. Сигнатура данного метода должна соответствовать сигнатуре, указанной при объявлении делегата. Поэтому в данном случае, если бы мы попытались инициализировать FirstStringMethod любым другим методом, сигнатура которого отличается от сигнатуры ToString(), возникла бы ошибка компиляции. Так как int.ToString() является методом экземпляра (а не статическим), для корректной инициализации делегата требуется вместе с именем метода указать экземпляр (х).<br />
Следующая строка кода использует делегата для отображения строкового значения. В любом коде указание имени делегата с последующими скобками, в которых перечислены параметры, имеет точно такое же действие, что и вызов метода, для которого делегат служит оболочкой. Поэтому в приведенном коде оператор Console.WriteLine() эквивалентен тому, что записано в комментарии.<br />
Одна из особенностей делегатов заключается в том, что они безопасны по типу в том смысле, что сигнатура вызываемого метода является корректной. Однако их совершенно не интересует, для какого типа объекта вызывается метод и является ли метод статическим или методом экземпляра.<br />
<center>
<div class="table_black">Экземпляр делегата может ссылаться на любой статический метод или метод экземпляра любого объекта любого типа — при условии, что сигнатура этого метода совпадает с сигнатурой делегата.</div>
<p></center><br />
Для демонстрации этого расширим приведенный выше пример кода, в котором используется делегат FirstStringMethod, чтобы он вызывал пару других методов для другого объекта: метод экземпляра и статический метод. Вновь обратимся к структуре Currency. Напомним, что структура Currency уже имеет свою собственную перегруженную версию ToString(). Добавим в Currency статический метод с такой же сигнатурой:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
struct Currency {<br />
	public static string GetCurrencyUnit(); {<br />
		return &#8220;Dollar&#8221;;<br />
	}</div>
<p>Теперь можно использовать экземпляр GetAString следующим образом:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
private delegate string GetAString();<br />
static void Main(string[] args) {<br />
	int X = 40;<br />
	GetAString FirstStringMethod = new GetAString(X.ToString);<br />
	Console.WriteLine(&#8221;String is &#8221; + FirstStringMethod());<br />
	Currency Balance = new Currency(34, 50);<br />
	FirstStringMethod = new GetAString(Balance.ToString);<br />
	Console.WriteLine(&#8221;String is &#8221; + FirstStringMethod());<br />
	FirstStringMethod = new GetAString(Currency.GetCurrencyUnit);<br />
	Console.WriteLine(&#8221;String is &#8221; + FirstStringMethod());</div>
<p>Этот код показывает, как можно вызвать метод при помощи делегата, и последовательно присваивает делегату ссылки на различные методы разных экземпляров классов: статические методы или методы экземпляров разных типов классов, при условии, что сигнатура метода совпадает с определением делегата.<br />
Однако мы все еще не продемонстрировали процесс собственно передачи делегата другому методу и не сделали ничего полезного. Метод ToString() для объектов int и currency можно вызывать более простым способом, без использования делегатов. Однако такова природа делегатов: чтобы оценить их полезность, необходимо рассмотреть сложный пример. Мы приведем два примера применения делегатов. Первый иллюстрирует, как передавать делегатов в методы и как можно использовать массивы делегатов — но в целом он не выполняет ничего такого, что нельзя было бы сделать без делегатов. Более сложный пример — класс BubbleSorter — реализует метод для сортировки массивов объектов в порядке возрастания. Этот класс было бы трудно написать без применения делегатов.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2663</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Делегаты.</title>
		<link>http://progclub.ru/?p=2661</link>
		<comments>http://progclub.ru/?p=2661#comments</comments>
		<pubDate>Mon, 10 May 2010 18:48:17 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2661</guid>
		<description><![CDATA[Делегаты лучше всего рассматривать как новый тип объекта в С#, в некотором роде они похожи на классы. Они предназначены для ситуаций, когда требуется передать методы другим методам. Например:

int I = int.Parse (&#8221;99&#8243;);
Здесь вызывается статический метод Parse() из класса System.Int32. Этот метод принимает один параметр — для данного перегруженного варианта — строку. Можно рассматривать ситуацию следующим [...]]]></description>
			<content:encoded><![CDATA[<p>Делегаты лучше всего рассматривать как новый тип объекта в С#, в некотором роде они похожи на классы. Они предназначены для ситуаций, когда требуется передать методы другим методам. Например:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
int I = int.Parse (&#8221;99&#8243;);</div>
<p>Здесь вызывается статический метод Parse() из класса System.Int32. Этот метод принимает один параметр — для данного перегруженного варианта — строку. Можно рассматривать ситуацию следующим образом. Метод Parse() производит какие-то действия точно так же, как и другие методы, а именно, он преобразует некоторые данные в int. Однако для того, чтобы использовать этот метод, необходимо указать, что представляют собой значимые данные. Мы делаем это путем передачи параметра (или аргумента). Вот для чего в основном используются аргументы методов — метод берет данные, передаваемые ему в качестве аргументов, и выполняет над ними действия.<br />
<span id="more-2661"></span><br />
Мы так часто применяем передачу данных в качестве параметров, что даже не задумываемся об этом, и потому идея передачи методов в качестве параметров может звучать несколько странно. Однако возможны случаи, когда метод помимо обработки данных должен делать что-то еще с другим методом, и — усложним ситуацию — во время компиляции неизвестно, что это за метод. Эта информация доступна только во время исполнения, и, следовательно, ее нужно передавать в качестве параметра первому методу. Для пояснения разберем примеры:</p>
<ul>
<li>Запуск потоков. Потоки будут рассматриваться в другой статье. Здесь скажем только, что поток — это последовательность исполнения. Когда приложение начинает работу, оно последовательно выполняет инструкции в статическом методе Main(). В С# можно запустить новую последовательность исполнения, которая будет работать параллельно с той, что уже исполняется. Запускается поток при помощи метода</li>
<li>Start() экземпляра одного из базовых классов, System.Threading.Thread. Приложение должно с чего-то начать, и точкой отсчета является Main(). Аналогично, если вы желаете, чтобы компьютер начал новую последовательность исполнения, нужно указать ему, где начать эту последовательность. Необходимо передать ему подробную информацию о методе, в котором начнется исполнение, т.е. Thread.Start() должен принимать параметр, содержащий информацию для этого метода</li>
<li>Общие библиотечные классы. Разумеется, существует большое число библиотек, содержащих код для выполнения различных стандартных задач. Обычно библиотеки самодостаточны в том смысле, что вы точно знаете, как выполнить задачу на этапе написания библиотеки. Однако иногда задача содержит в себе некоторую дополнительную задачу, и только клиентский код знает, как ее выполнить. Ниже мы рассмотрим пример сортировки массива объектов. Мы напишем класс, который принимает массив объектов и сортирует их в порядке возрастания. Часть процесса сортировки заключается во взятии двух объектов из массива и их сравнении с целью определения, какой из них должен идти первым. Так как наш класс должен уметь сортировать массивы объектов любого типа, не существует способа, с помощью которого он сможет заранее определить, как производить это сравнение. Клиентский код, который передает нашему классу массив объектов, должен также сообщить классу, как выполнять сравнение для конкретных объектов, которые необходимо отсортировать. Другими словами, клиентский код должен будет передать нашему классу сведения о методе, который можно вызвать для выполнения сравнения.</li>
<li>События. События играют важную роль в программировании Windows GUI. Идея состоит в том, что часто требуется информировать некоторый код о том, что произошли те или иные события. Например, при наборе текста в Microsoft Word каждый раз, когда нажимается клавиша, Word должен быть информирован об этом, чтобы он смог выполнить соответствующее действие. (Обычно это означает добавление соответствующей буквы в документ и отображение ее на экране.) Каждый раз при нажатии на клавишу Windows вызывает конкретный метод внутри Word, который осуществляет соответствующее действие, т.е. обрабатывает событие. Для того чтобы Windows могла сделать это, Word в некоторый момент должен сообщить Windows о том, какой метод необходимо вызвать в ответ на нажатие клавиши. Поэтому Word должен вызвать функцию API и передать ей подробности об этом методе. В очередной раз мы видим, что сведения о методе должны быть переданы в качестве параметра. Очевидно, что, программируя на С#, мы будем иметь дело со средой исполнения .NET, а не с Windows, но когда мы приступим к программированию GUI с помощью Windows Forms, то используем те же самые принципы: код должен будет сообщать среде исполнения .NET о том, какие методы обрабатывают какие события. Чтобы сделать это, необходимо будет передавать подробную информацию о методах в качестве параметров другим методам.</li>
</ul>
<p>Итак, в ряде случаев методы должны принимать сведения о других методах в качестве параметров. Теперь выясним, как это сделать. Наиболее простой способ — передача имени метода в качестве параметра. Возвращаясь к примеру с потоками, допустим, что мы собираемся запустить новый поток и что имеется метод EntryPoint, в котором должно начаться исполнение нового потока:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
void EntryPoint() {<br />
 // выполнение, того, что должен делать новый поток<br />
}</div>
<p>Могли бы мы начать новый поток, используя следующий код?</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Thread NewThread = new Thread ();<br />
Thread.Start(EntryPoint); // Неверно</div>
<p>Это наиболее простой способ ведения дел, и некоторые языки, например С и С++, именно так и поступают в подобной ситуации (в С и С++ параметр EntryPoint является указателем функции). Кстати говоря, что-то подобное происходит за кулисами в Visual Basic при добавлении обработчиков событий, однако среда исполнения Visual Basic настолько хорошо ограждает вас от деталей происходящего, что вы никогда не догадывались об этом.<br />
К сожалению, такой прямой подход вызывает некоторые проблемы с безопасностью типов, а также игнорирует тот факт, что в объектно-ориентированном программировании методы редко существуют в изоляции и обычно должны быть ассоциированы с экземпляром класса перед вызовом. Из-за этих проблем С# не разрешает использовать этот подход. Если требуется передавать методы в качестве параметров, сведения о методе необходимо поместить в новый тип объекта — делегат. Делегаты являются особым типом объекта — особым в том смысле, что они содержат не данные, а информацию о методе.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2661</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Множественное приведение типов.</title>
		<link>http://progclub.ru/?p=2659</link>
		<comments>http://progclub.ru/?p=2659#comments</comments>
		<pubDate>Mon, 10 May 2010 18:47:53 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2659</guid>
		<description><![CDATA[Одна из ситуаций, которую необходимо отслеживать при определении приведений типов, заключается в том, что если не будет существовать прямого приведения типов для выполнения требуемого преобразования, компилятор С# попытается найти способ совмещения нескольких приведений. Например, обращаясь к структуре Currency, предположим, что компилятор встречает такие строки кода:

Curency Balance = new Currency(10, 50);
long Amount = (long)Balance;
double AmountD = [...]]]></description>
			<content:encoded><![CDATA[<p>Одна из ситуаций, которую необходимо отслеживать при определении приведений типов, заключается в том, что если не будет существовать прямого приведения типов для выполнения требуемого преобразования, компилятор С# попытается найти способ совмещения нескольких приведений. Например, обращаясь к структуре Currency, предположим, что компилятор встречает такие строки кода:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Curency Balance = new Currency(10, 50);<br />
long Amount = (long)Balance;<br />
double AmountD = Balance;</div>
<p><span id="more-2659"></span><br />
Сначала инициализируется экземпляр Currency, затем мы пытаемся привести его к long. Проблема заключается в том что такой тип преобразования не определен. Однако код будет успешно компилироваться. Произойдет следующее: компилятор поймет, что вы определили неявное приведение Currency в float, а он знает, как явно привести float к long. Он откомпилирует эту строку в код на промежуточном языке, который сначала преобразует balance в float, а затем преобразует результат в long. То же самое происходит и в последней строке кода при преобразовании Balance в double. Однако так как приведения Currency в float и float в double являются неявными, то и в коде это преобразование можно записать неявно. При желании последовательность приведений можно указать явно:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new Currency(1O, 50);<br />
long Amount = (long)(float)Balance;<br />
double AmountD = (double)(float)Balance;</div>
<p>Однако в большинстве случаев это лишь усложнит ваш код. Следующий код приведет к ошибке компиляции:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new Currency(1O, 50);<br />
long Amount = Balance;</div>
<p>Дело в том, что наилучшим путем преобразования, который удается найти компилятору, по-прежнему является преобразование сначала в float, а затем в long. Но преобразование float в long должно быть указано явно. Даже такой код вызовет ошибку:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new, Currency(1O, 50);<br />
long Amount = (float)Balance;</div>
<p>В данном случае мы (бесполезно) явно осуществили первую часть преобразования, но не сделали этого для преобразования float-long.<br />
Само по себе это не должно создавать особых проблем. Эти правила понятны и предназначены для того, чтобы предотвратить потери данных, о которых разработчик может и не знать. Однако если вы не будете осторожны при определении своих собственных приведений типов, компилятор может осуществить такое приведение, что вы получите неожиданные результаты. Допустим, что кому-то еще в группе, занимающейся разработкой класса Currency, приходит в голову мысль, что было бы полезно иметь возможность преобразования uint, содержащего общее количество центов, в Currency (центов, а не долларов, так как нельзя терять доли доллара). Поэтому может быть написано преобразование:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
// не делайте этого!<br />
public static implicit operator Currency (uint value) {<br />
	return new Currency (value/1O0u, (ushort) (value%100) );<br />
}</div>
<p>Отметим, что u, идущее за первым числом 100, гарантирует, что value/100u будет интерпретироваться как uint. Если бы мы написали value/100, компилятор интерпретировал бы это выражение как int.<br />
В коде мы явно указали &#8220;Не делайте этого&#8221;, и вот почему. Посмотрите на следующий фрагмент кода: все, что мы делаем,— преобразуем uint, содержащее 350. в Currency и обратно. Что, по-вашему, будет находиться в Ваl2 после выполнения кода?</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
uint Bal = 350;<br />
Currency Balance = Bal;<br />
uint Bal2 = (uint)Balance;</div>
<p>Ответом будет не 350, a 3! И здесь все логично. Мы явно преобразуем 350 в Currency, получая в результате Balance.Dollars=3, Balance.Cents=50. Затем компилятор как обычно определяет наилучший путь для обратного преобразования. В результате Balance неявно преобразуется в float (значение 3.5), и уже это значение явно преобразуется в uint — получаем 3.<br />
Имеются и другие типы данных, для которых преобразование из одного типа данных в другой, а затем обратно вызывает потерю данных. Например, преобразование float, содержащего 5.8, в int, а затем обратно в float приведет к потере дробной части, результатом будет 5. Однако существует принципиальная разница между потерей дробной части и &#8220;случайным&#8221; делением числа более чем на 100! Currency неожиданно стал опасным классом, который вытворяет странные вещи с целыми числами!<br />
Проблема состоит в том, что имеется конфликт в интерпретации целых чисел нашими приведениями. Приведения между Currency и float интерпретируют целое число 1 как один доллар, а последнее приведение uint-Currency интерпретирует это значение как один цент. Это пример плохой разработки. Если вы хотите, чтобы ваши классы были пригодны для использования, вам придется убедиться в том, что все ваши приведения типов взаимозаменяемы в гом смысле, что они приводят к одинаковым результатам. В данном случае совершенно очевидно, что необходимо переписать приведение uint-Currency так. чтобы оно интерпретировало целое число 1 как один доллар:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
// Верно<br />
public static implicit operator Currency (uint value) {<br />
	return new Currency(value, 0) ;<br />
}</div>
<p>В связи с этим возникает вопрос: зачем вообще нужно такое приведение? Дело в том, что без этого приведения единственным способом для компилятора осуществить приведение uint-Currency было бы приведение через float. В данном случае прямое преобразование является более эффективным, поэтому дополнительное приведение увеличивает производительность. Однако необходимо убедиться в том, что оно дает те же результаты, которые могут быть получены при использовании приведения через float. Вы можете встретить ситуации, в которых отдельное объявление преобразований для различных предопределенных типов данных позволяет больше преобразований делать неявно.<br />
Для проверки совместимости приведения типов следует выяснить, приводят ли преобразования к одним и тем же результатам (за исключением, возможно, потери точности, как в преобразовании float-int), независимо от того, каким способом они осуществляются. Хорошим примером может быть класс Currency. Рассмотрим код:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new Currency(50, 35);<br />
ulong Bal = (ulong) Balance;</div>
<p>В настоящий момент существует только один способ, которым компилятор может выполнить это действие: преобразовав Currency неявно в float, а затем явно в ulong. Преобразование float-ulong требует явного приведения, в данном случае мы его указали.<br />
Однако допустим, что мы добавляем другое приведение для неявного преобразования Currency в uint. Мы сделаем это, изменив структуру Currency и добавив преобразования как в, так и из uint. Этот код содержится в примере SimpleCurrency2:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
// Верно!<br />
public static implicit operator Currency (uint value) {<br />
	return new Currency(value, 0);<br />
}<br />
public static implicit operator uint (Currency value) {<br />
	return value.Dollars;<br />
}</div>
<p>Теперь у компилятора имеется другая возможность преобразования из Currency в ulong: преобразовать неявно Currency в uint, а затем неявно в ulong. Какой из этих двух путей он выберет? Разумеется, в С# существуют четкие правила (которые мы не будем описывать в этой книге, их можно найти в документации MSDN), определяющие, как компилятор выбирает наилучший путь из нескольких возможных. Вы должны разрабатывать свои приведения типов так, чтобы все маршруты приведения давали один и тот же результат (за исключением возможной потери точности), тогда будет не важно, какой из них выберет компилятор. (В данном случае компилятор выбирает маршрут Currency->uint->ulong, а не Currency->float->ulong.)<br />
Для проверки примера SimpleCurrency2 добавим следующий код в тестовую программу:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
try{<br />
	Currency balance = new Currency(50, 35);<br />
	Console.WriteLine(balance) ;<br />
	Console.WriteLine(&#8221;balance is &#8221; * balance);<br />
	Console.WriteLine(&#8221;balance is (using ToString()) &#8221; + balance.ToString()) ;<br />
	uint balance3 = (uint) balance;<br />
	Console.WriteLine(&#8221;Converting to uint gives &#8221; + balance3); </div>
<p>Преобразование в uint было успешным, хотя, как и ожидалось, мы потеряли при этом центы. Приведение отрицательного значения float к Currency вызвало ожидаемое исключение переполнения, так как теперь само преобразование float-Currency осуществляется в checked-контексте.<br />
Однако результат показывает одну потенциальную проблему, которую следует учитывать при приведении типов. Самая первая строка некорректно вывела значение balance, показав 50 вместо $50.35. Из этих строк:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Console.WriteLine(balance);<br />
Console.WriteLine(&#8221;balance is &#8221; + balance);<br />
Console.WriteLine(&#8221;balance is (using ToString() &#8221; + balance.ToString ());</div>
<p>только две последние строки правильно отобразили Currency как строку. Что же произошло?<br />
Проблема заключается в том, что совмещение приведений типов с перегрузками методов может быть непредсказуемым.<br />
Рассмотрим эти три строки в обратном порядке. Третий оператор Console.WriteLine() явно вызывает метод Currency.ToString(), гарантируя, что Currency отобразится как строка. Второй оператор не делает этого. Однако строковый литерал &#8220;balance is &#8220;, который передается в Console.WriteLine(), ясно показывает компилятору, что параметр должен интерпретироваться как строка. Поэтому метод Currency.ToString() будет вызван неявно.<br />
Самый первый метод Console.WriteLine() передает структуру Currency в Console.WriteLine(). Console.WriteLine() имеет большое число перегруженных версий, но ни одна из них не принимает структуру Currency. Поэтому компилятор начнет выяснять, как можно привести Currency, чтобы она соответствовапа одному из перегруженных методов Console.WriteLine(). Оказывается, что один из перегруженных методов Console.WriteLine() разработан специально для быстрого и эффективного отображения uint и принимает uint в качестве параметра. А мы только что определили приведение, которое неявно преобразует Currency в uint. Об остальном вы можете догадаться сами.<br />
Кстати говоря, Console.WriteLine() имеет еще один перегруженный метод, который принимает в качестве параметра double и отображает его значение. Если вы внимательно посмотрите на результаты первого примера SimpleCurrency, то обнаружите, что первая строка отобразила Currency как double, используя этот перегруженный метод. В том примере отсутствовало прямое приведение Currency в uint, поэтому компилятор выбрал путь Currency->Eloat->double как предпочтительный при поиске подходящего перегруженного метода Console.WriteLine(). Но сейчас, имея возможность прямого преобразования в uint, компилятор предпочел использовать его.<br />
Результатом является то. что если у вас имеется метод, для которого существует несколько перегруженных вариантов, и вы пытаетесь передать в него параметр, чей тип данных не соответствует точно типу данных ни в одном из этих перегруженных методов, то вы заставляете компилятор не только выбирать приведения типов для преобразования данных, но и какой перегруженный метод — а, следовательно, и какие преобразования — использовать. Разумеется, компилятор всегда поступает логично и в соответствии со строгими правилами. Однако результат может оказаться не тем, который ожидается. Если существуют какие-то сомнения, лучше явно указывать приведения типов.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2659</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Упаковывающие и распаковывающие приведения типов.</title>
		<link>http://progclub.ru/?p=2657</link>
		<comments>http://progclub.ru/?p=2657#comments</comments>
		<pubDate>Mon, 10 May 2010 18:01:12 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2657</guid>
		<description><![CDATA[Выше мы рассматривали приведение типов между базовым и производным классами, которые оба являлись типами по ссылке. Аналогичные принципы применимы при приведении типов по значению, хотя в этом случае недостаточно скопировать ссылки — необходимо также скопировать некоторые данные.
Разумеется, невозможно осуществлять наследование от структур или примитивных типов по значению. Поэтому приведение типа между базовой и производной структурами [...]]]></description>
			<content:encoded><![CDATA[<p>Выше мы рассматривали приведение типов между базовым и производным классами, которые оба являлись типами по ссылке. Аналогичные принципы применимы при приведении типов по значению, хотя в этом случае недостаточно скопировать ссылки — необходимо также скопировать некоторые данные.<br />
Разумеется, невозможно осуществлять наследование от структур или примитивных типов по значению. Поэтому приведение типа между базовой и производной структурами неизменно означает приведение типов между примитивным типом или структурой и System.Object. (Теоретически можно приводить типы между структурой и System.ValueType, хотя трудно сказать, зачем это нужно.)<br />
Приведение любой структуры (или примитивного типа) к object всегда доступно в качестве неявного приведения — так как это приведение от производного типа к базовому — и является знакомым процессом упаковки, который был коротко рассмотрен в другой статье. Например, для нашей структуры Currency:<br />
<span id="more-2657"></span></p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new Currency(40, 0);<br />
object BaseCopy = Balance;</div>
<p>При выполнении неявного приведения содержимое Balance копируется в кучу, а ссылка объекта BaseCopy будет указывать на него. На самом деле происходит следующее: когда мы первоначально определяем структуру Currency, платформа .NET неявно создает другой (скрытый) класс — упакованный класс Currency, который содержит те же самые поля, что и структура Currency, но при этом является типом данных по ссылке, хранящимся в куче. Это происходит при определении любого типа по значению — не важно, структуры или перечисления,— и аналогичные упакованные типы по ссылке существуют соответственно для всех примитивных типов по значению: int, double, uint и т.п. К этим упакованным классам в исходном коде невозможно получить прямой доступ никакими способами, но именно эти объекты используются тогда, когда тип по ссылке приводится к object. Когда мы явно приводим Currency к object, создается экземпляр ограниченного класса Currency, который инициализируется данными из структуры Currency. Именно на этот упакованный экземпляр Currency будет ссылаться BaseCopy. Это означает, что синтаксически приведение производного типа к базовому для типов по значению работает так же, как и для типов по ссылке.<br />
Обратное приведение известно как распаковка. Так же, как приведение типов между базовым типом по ссылке и производным типом по ссылке, оно является явным. Если объект имеет недопустимый тип, будет сгенерировано исключение:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Object DerivedObject = new Currency (40, 0);<br />
object BaseObject = new object ();<br />
Currency DerivedCopyl = (Currency)DerivedObject; // все нормально<br />
Currency DerivedCopy2 = (Currency)BaseObject; // генерируется исключение</div>
<p>Этот код работает аналогично коду, показанному ранее для типов по ссылке. Приведение DerivedObject к Currency выполняется без проблем, так как DerivedObject на самом деле ссылается на упакованный экземпляр Currency — приведение будет осуществлено путем простого копирования полей из упакованного объекта Currency в новую структуру Currency. Второе приведение не срабатывает, поскольку BaseObject не ссылается на упакованный экземпляр Currency.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2657</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Приведения типов между базовыми и производными классами.</title>
		<link>http://progclub.ru/?p=2655</link>
		<comments>http://progclub.ru/?p=2655#comments</comments>
		<pubDate>Mon, 10 May 2010 17:49:47 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2655</guid>
		<description><![CDATA[Начнем со случая, когда источник и приемник являются типами по ссылке, и рассмотрим два класса: MyBase и MyDerived, где MyDerived явно или неявно унаследован от MyBase.
Что касается преобразования из MyDerived в MyBase, всегда можно записать (предполагая, что доступны конструкторы):

MyDerived DerivedObject = new MyDerived();
MyBase BaseCopy = DerivedObject;
В данном случае выполняется неявное приведение MyDerived в MyBase, что [...]]]></description>
			<content:encoded><![CDATA[<p>Начнем со случая, когда источник и приемник являются типами по ссылке, и рассмотрим два класса: MyBase и MyDerived, где MyDerived явно или неявно унаследован от MyBase.<br />
Что касается преобразования из MyDerived в MyBase, всегда можно записать (предполагая, что доступны конструкторы):</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
MyDerived DerivedObject = new MyDerived();<br />
MyBase BaseCopy = DerivedObject;</div>
<p>В данном случае выполняется неявное приведение MyDerived в MyBase, что допустимо, так как существует правило, по которому ссылка на тип MyBase может ссылаться на объекты класса MyBase или любые производные от него объекты. Это возможно, поскольку экземпляры производного класса представляют собой экземпляры базового класса плюс некоторая добавка. Все функции и поля, объявленные в базовом классе, определены и в производном классе.<br />
Далее можно записать:<br />
<span id="more-2655"></span></p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
MyBase DerivedObject = new MyDerived();<br />
MyBase BaseObject = new MyBase();<br />
MyDerived DerivedCopyl = (MyDerived) DerivedObject; // верно<br />
MyDerived DerivedCopy2 = (MyDerived) BaseObject; // вызовет исключение</div>
<p>Этот код является допустимым в С#, он иллюстрирует приведение типа базового класса к типу производного класса. Однако при выполнении последнего оператора возникнет исключение. В этом случае приводимый объект сначала проверяется. Так как ссылка на базовый класс может в принципе ссылаться на экземпляр производного класса, вполне возможно, что этот объект на самом деле является экземпляром производного класса, для которого мы пытаемся осуществить приведение. В этом случае приведение выполняется успешно — ссылка на производный объект будет ссылаться на этот объект. Однако если объект не является экземпляром производного класса (или любого класса, являющегося производным от него), преобразования типа не происходит и генерируется исключение.<br />
Отметим, что приведения типов, осуществляемые компилятором между базовым и производным классами, на самом деле не выполняют никаких преобразований данных. Все, что они делают,— устанавливают новую ссылку на объект, если преобразование возможно. В этом смысле они отличаются от тех приведений типов, которые вы определяете самостоятельно. Так, в примере SimpleCurrency мы определили приведения, которые осуществляют преобразование между структурой Currency и float. В приведении float-Currency мы создали новый экземпляр структуры Currency и инициализировали его требуемыми значениями. Аналогично, в примере иерархии классов, если бы мы определили приведение типов из С в D, то реализация выглядела бы так:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public static implicit operator D(C value){<br />
	D Result = new D();<br />
	// Код для инициализации D соответствующими значениями<br />
	// в зависимости от содержимого С<br />
	return D;<br />
}</div>
<p>Здесь опять создается экземпляр нового объекта со значениями, основанными на значениях старого объекта. Предопределенные приведения типов между базовым и производным классом не делают этого. Если вам необходим такой вид функциональности, т.е. действительно требуется преобразовать экземпляр MyBase в объект MyDerived со значениями, основанными на содержимом экземпляра MyBase, этот синтаксис приведения использовать нельзя — придется написать метод для такого преобразования:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
class Base{<br />
public Derived ToDerived() (<br />
	Derived Result = new Derived();<br />
	// инициализируем Result значениями на основе Base<br />
	rerutn Result;<br />
}</div>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2655</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Приведение типов между классами.</title>
		<link>http://progclub.ru/?p=2651</link>
		<comments>http://progclub.ru/?p=2651#comments</comments>
		<pubDate>Tue, 13 Apr 2010 17:07:16 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2651</guid>
		<description><![CDATA[В примере Currency использовались только классы, которые преобразовывались в или из float — один из предопределенных типов данных. Однако применение простых типов данных не является жестким требованием. Можно объявить преобразование типов для приведения типов классов или структур, объявленных в коде. Однако существует несколько ограничений:


Нельзя осуществлять приведение типов между классами, один из которых явно или неявно [...]]]></description>
			<content:encoded><![CDATA[<p>В примере Currency использовались только классы, которые преобразовывались в или из float — один из предопределенных типов данных. Однако применение простых типов данных не является жестким требованием. Можно объявить преобразование типов для приведения типов классов или структур, объявленных в коде. Однако существует несколько ограничений:<br />
<span id="more-2651"></span></p>
<ul>
<li>Нельзя осуществлять приведение типов между классами, один из которых явно или неявно является производным от другого.</li>
<li>Приведение типа должно быть объявлено внутри определения либо типа- источника, либо типа-приемника.</li>
</ul>
<p>Для иллюстрации этих требований рассмотрим ситуацию:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
class А<br />
{<br />
	// реализация<br />
}<br />
class В : public А<br />
{<br />
	// реализация<br />
}<br />
class С : public В<br />
{<br />
	// реализация<br />
}<br />
class D ; public В<br />
{<br />
	// реализация<br />
}</div>
<p>В этой ситуации совершенно законным будет объявление приведения типов для преобразования между С и D, так как эти классы не наследуются друг от друга. Код для этого мог бы быть таким (если требуется, чтобы приведения типов были неявными):</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public static implicit operator D (С value){<br />
	// и т.д.<br />
}<br />
public static, implicit operator С (D value){<br />
	// и т.д.<br />
}</div>
<p>Каждое из этих приведений можно поместить либо внутри класса С, либо внутри класса D, но нигде более. С# требует, чтобы определение приведения типа помещалось либо в класс-источник (или структуру-источник), либо в класс-приемник. Побочным эффектом является то, что невозможно определить приведение типов для двух классов, если нельзя получить доступ к исходному коду хотя бы одного из них. Это существенно, так как не позволяет сторонним лицам объявлять приведения типов для ваших классов.<br />
Другим моментом является то, что как только приведение типов объявлено в одном из классов, его уже невозможно объявить в другом классе. Совершенно очевидно, что для каждого преобразования должно существовать только одно определение, иначе компилятор не сможет решить, какое из определений выбрать.<br />
Для нашей иерархии можно объявить только приведения из С в D и из D в С. Все остальное (например, из А в С или из D в В) вызовет ошибку компиляции, поскольку С# не разрешает пользовательские приведения типов для производных друг от друга классов. Причина этого проста: такие приведения уже существуют. Всегда можно неявно привести производный класс к базовому, а базовый явно к производному. Как это сделать, описывается ниже.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2651</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Пример: структура Currency.</title>
		<link>http://progclub.ru/?p=2649</link>
		<comments>http://progclub.ru/?p=2649#comments</comments>
		<pubDate>Tue, 13 Apr 2010 17:03:08 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2649</guid>
		<description><![CDATA[Рассмотрим применение определяемых пользователем явных и неявных преобразований на примере SimpleCurrency (который можно загрузить с сайта). В этом примере объявляется структура Currency, которая содержит положительную сумму в долларах США. Вообще говоря, С# для таких целей предлагает тип decimal, но вы можете создать свой собственный класс или структуру для представления денежных сумм, если требуется выполнять сложные [...]]]></description>
			<content:encoded><![CDATA[<p>Рассмотрим применение определяемых пользователем явных и неявных преобразований на примере SimpleCurrency (который можно загрузить с сайта). В этом примере объявляется структура Currency, которая содержит положительную сумму в долларах США. Вообще говоря, С# для таких целей предлагает тип decimal, но вы можете создать свой собственный класс или структуру для представления денежных сумм, если требуется выполнять сложные финансовые вычисления и реализовывать для этого специальные методы.<br />
<span id="more-2649"></span><br />
<center>
<div><i>Синтаксис преобразования типов одинаков для структур и классов. В нашем примере применяется структура. Однако все то же самое было бы справедливо и в случае, если бы мы объявили Currency классом.</i></div>
<p></center></p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
struct Currency {<br />
	public uint Dollars;<br />
	public ushort Cents;<br />
	public Currency(uint Dollars, ushort Cents){<br />
		this.Dollars = Dollars;<br />
		this.Cents = Cents;<br />
	}<br />
	public override String ToString(){<br />
		return String.Format(&#8221;${0}.{1,-2:00)&#8221;, Dollars, Cents);<br />
	}<br />
}</div>
<p>Применение типов данных без знака для Dollars и Cents гарантирует то, что экземпляр Currency может содержать только положительные значения. Мы ограничиваем здесь допустимые значения для того, чтобы позже показать некоторые особенности явных приведений типов. Этот класс можно использовать, например, для хранения сведений о зарплате сотрудников компании. (Зарплаты не бывают отрицательными!) Для упрощения класса его поля объявлены открытыми, но обычно они объявляются закрытыми и для каждого определяется соответствующее свойство.<br />
Начнем с того, что нам нужно преобразовывать экземпляры Currency в значения float, где целая часть float представляет доллары. Другими словами, мы хотели бы записать код следующим образом:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
Currency Balance = new Currency(10, 50);<br />
float D = Balance; // Нужно, чтобы D было присвоено 10.5</div>
<p>Чтобы иметь такую возможность, необходимо определить приведение типа. К определению Currency добавим следующее:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
struct Currency {<br />
	// и т.д.<br />
	public static implicit operator float (Currency value){<br />
		return value.Dollars + (value.Cents/100.Of);<br />
	}<br />
}</div>
<p>Это неявное приведение типа. В данном случае это допустимо, так как из определения Currency ясно, что любое значение, способное в нем храниться, может также храниться и в float. Преобразование всегда будет работать правильно.<br />
<i>Здесь мы немного лукавим &#8211; наше определение &#8220;всегда будет работать правильно&#8221; является слишком широким: на самом деле при преобразовании из uint в float может происходить потеря точности, но Microsoft посчитала эту ошибку незначительной и объявила приведение int-e-float неявным.</i><br />
А как насчет обратного преобразования? У нас есть величина с плавающей точкой — float, и ее требуется преобразовать в Currency. В данном случае не гарантируется, что преобразование будет работать, так как float способен хранить отрицательные значения, которые не могут храниться в экземплярах Currency, и, кроме того, float может хранить числа гораздо большей величины, чем поле (uint) Dollar в Currency. В том случае, если float содержит неподходящее значение, приведение его к Currency может дать непредсказуемый результат. Из-за этого риска преобразование из float в Currency должно быть объявлено явным. Вот наша первая попытка:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public static explicit operator Currency (float value) {<br />
	uint Dollars = (uint)value;<br />
	ushort Cents = (ushort) ((value-Dollars) *100);<br />
	return new Currency(Do)Iars, Cents);<br />
}</div>
<p>В данном коде мы сначала получаем число долларов как целую часть переданной величины. Допустим, что передано значение 46.63Е. Преобразование его в uint даст 45 — число долларов. Разница между этим числом и оригинальным float равна 0.63. Умножив ее на 100, мы получим число центов.<br />
Этот код будет успешно откомпилирован:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
float Amount = 45.63f;<br />
Currency Amount2 = (Currency)Amount;</div>
<p>Однако следующий код сгенерирует ошибку компиляции, так как делается попытка неявно использовать явное приведение типа:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
float Amount = 45.63f;<br />
Currency Amount2 = Amount; // неверно</div>
<p>Хорошо ли это? Помечая приведение типа как явное, вы предупреждаете разработчика о том, что может произойти потеря данных. Но в нашем случае это, вероятно, не то, что требуется. Напишем и запустим тестовый пример. Метод Main() создает экземпляр структуры Currency и пытается вьшолнить несколько приведений типов. В начале кода величина balance записана двумя разными способами (это понадобится нам позже):</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
static void Main(string[] args){<br />
	try{<br />
		Currency balance = new Currency(50, 35);<br />
		Console.WriteLine(&#8221;balance is &#8221; + balance);<br />
		Console.WriteLine(&#8221;balance is (using ToString())&#8221; + balance.ToString());<br />
		float balance2 = balance;<br />
		Console.WriteLine(&#8221;After converting to float, = &#8221; + balance2);<br />
		balance = (Currency)balance2;<br />
		Console.WriteLine(&#8221;After converting back to Currency, = &#8221; + balance);<br />
		Console.WriteLine(&#8221;Row attempt to convert out of range value of &#8221; + &#8220;-$100.00 to a Currency:&#8221;);<br />
		checked {<br />
			balance = (Currency)(-50.5);<br />
			Console.WriteLine(&#8221;Result is &#8221; + balance.ToString());<br />
		}<br />
	}<br />
	catch (Exception e){<br />
		Console.WriteLine(&#8221;Exception occurred: &#8221; + e.Message);<br />
	}<br />
}</div>
<p>Отметим, что весь код размещен в блоке try для обработки любых исключений, которые могут возникнуть во время преобразований. Мы также поместили строки, преобразующие неверное значение в Currency, в блок checked, поэтому отрицательные значения должны обрабатываться. А будут ли? При исполнении этого кода получаем результат:<br />
Большая часть кода работает нормально, однако исключение не было сгенерировано. Проблему легко обнаружить. Место, в котором происходит переполнение, находится не в методе Main() — оно внутри кода для операции приведения, которая вызывается из метода Main(). А этот код не был отмечен как checked.<br />
Одно из решений заключается в обеспечении того, что само приведение должно производиться в контексте checked:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public static explicit operator Currency (float value){<br />
	checked {<br />
		uint Dollars = (uint)value; // при отрицательном значении происходит переполнение<br />
		ushort Cents = (ushort)((value-Dollars)*100);<br />
		return new Currency(Dollars, Cents);<br />
	}<br />
}</div>
<p>Это гарантирует, что исключение будет корректно сгенерировано (даже если вызывающий код не является checked). В качестве альтернативы можно сгенерировать свое собственное исключение внутри операции приведения. Для этого потребуется написать дополнительный код, но вы сможете передать более детальную информацию об ошибке. Здесь мы не будем рассматривать этот вариант.<br />
<i>Разумеется, при объявлении приведения типов, которое будет часто использоваться и для которого весьма важна производительность, вы можете предпочесть вообще не осуществлять проверку на ошибки. Это вполне приемлемое решение при условии, что поведение преобразования и отсутствие обработки ошибок будет хорошо документировано.</i><br />
В нашем примере осуществляется преобразование из или в float, однако для других типов данных синтаксис аналогичен. Кроме того, тем же синтаксисом вы будете пользоваться, если потребуется объявить приведение типа для двух классов или структур. Преобразование типа не обязано применять один из предопределенных типов данных.</p>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2649</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Определенные пользователем приведения типов.</title>
		<link>http://progclub.ru/?p=2647</link>
		<comments>http://progclub.ru/?p=2647#comments</comments>
		<pubDate>Tue, 13 Apr 2010 16:52:51 +0000</pubDate>
		<dc:creator>devalmor</dc:creator>
				<category><![CDATA[Статьи по С#]]></category>

		<guid isPermaLink="false">http://progclub.ru/?p=2647</guid>
		<description><![CDATA[В предыдущих статьях говорилось о том, как конвертировать значения между различными предопределенными типами данных. Это осуществляется с помощью процесса приведения типов. С# допускает два различных вида приведения типов: явное и неявное. Разница состоит в том, что явное преобразование необходимо явно отметить в коде, заключив тип данных назначения в круглые скобки:


int I = 3;
long L = [...]]]></description>
			<content:encoded><![CDATA[<p>В предыдущих статьях говорилось о том, как конвертировать значения между различными предопределенными типами данных. Это осуществляется с помощью процесса приведения типов. С# допускает два различных вида приведения типов: явное и неявное. Разница состоит в том, что явное преобразование необходимо явно отметить в коде, заключив тип данных назначения в круглые скобки:<br />
<span id="more-2647"></span></p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
int I = 3;<br />
long L = I; // неявное<br />
short S = (short) I; // явное</div>
<p>Для предопределенных типов данных явное приведение типа требуется в том случае, если при приведении типа существует риск ошибки или потери данных — например, при преобразовании из int в short, так как short может не вместить в себя значение int. Кроме того, преобразование типов данных со знаком в типы данных без знака может привести к ошибке в том случае, если переменная со знаком содержит отрицательное число, а при преобразовании чисел с плавающей точкой в целочисленные типы данных теряется их дробная часть. Идея явного указания таких приведений заключается в том, что вы тем самым сообщаете компилятору о своем понимании риска потери данных и о том, что вы написали свой код с учетом такой возможности.<br />
Так как С# позволяет определять свои собственные структуры и классы, что означает создание собственных типов данных, можно предположить, что существует возможность приведения и для собственных типов данных. С# действительно позволяет делать это. Me ханизм заключается в том, что можно объявить приведение как операцию одного из классов. Приведение должно быть отмечено как явное или неявное в зависимости от того, как оно должно использоваться. Ожидается, что вы будете следовать тем же рекомендациям, что и для предопределенных приведений типов: если вы знаете, что приведение типа всегда будет безопасным, какое бы значение ни содержалось в исходной переменной, его можно объявить неявным. Если же существует риск того, что для тех или иных значений что-то будет происходить не так — возможно, потеря данных или генерация исключения,— тогда необходимо определить преобразование как явное.<br />
<center>
<div class="table_black">Собственные приведения типов необходимо объявлять явными в том случае, если возможно возникновение ошибки приведения или генерация исключения для некоторых значений исходной переменной.</div>
<p></center><br />
Синтаксис для определения приведения типа аналогичен тому, что применяется для перегрузки операций. Это не случайное совпадение, так как приведение типа может рассматриваться как операция, чье действие заключается в преобразовании типа данных источника в тип данных назначения. Для пояснения приведем фрагмент кода:</p>
<div style="border: 0.5pt solid windowtext; padding: 10pt 4pt; background: #cccccc none repeat scroll 0% 0%;">
public static implicit operator float (Curency value)<br />
{<br />
// и т.д.</div>
<p>Код является частью структуры Currency, предназначенной для хранения денежных сумм. Объявленное здесь приведение типа позволяет неявно преобразовать величину Currency в float. Отметим, что если приведение объявлено неявным, компилятор позволит использовать его как в явной, так и в неявной форме. Если же преобразование объявлено явным, компилятор разрешит использовать его только таким образом.<br />
Здесь преобразование объявлено статическим. Как и в случае перегрузки операций, С# требует, чтобы приведения типов были статическими. Это означает, что каждое приведение типа принимает один параметр, который является типом данных источника.</p>
<div align="right"><i>Разработчики на С++ отметят, что это прямая противоположность С++, в котором приведения типов являются членами экземпляров классов.</i></div>
]]></content:encoded>
			<wfw:commentRss>http://progclub.ru/?feed=rss2&amp;p=2647</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
