<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://wiki2.linuxformat.ru/index.php?action=history&amp;feed=atom&amp;title=LXF143%3Ac</id>
		<title>LXF143:c - История изменений</title>
		<link rel="self" type="application/atom+xml" href="http://wiki2.linuxformat.ru/index.php?action=history&amp;feed=atom&amp;title=LXF143%3Ac"/>
		<link rel="alternate" type="text/html" href="http://wiki2.linuxformat.ru/index.php?title=LXF143:c&amp;action=history"/>
		<updated>2026-05-14T04:16:33Z</updated>
		<subtitle>История изменений этой страницы в вики</subtitle>
		<generator>MediaWiki 1.11.1</generator>

	<entry>
		<id>http://wiki2.linuxformat.ru/index.php?title=LXF143:c&amp;diff=14256&amp;oldid=prev</id>
		<title>Crazy Rebel: викификация, оформление</title>
		<link rel="alternate" type="text/html" href="http://wiki2.linuxformat.ru/index.php?title=LXF143:c&amp;diff=14256&amp;oldid=prev"/>
				<updated>2012-07-09T08:44:27Z</updated>
		
		<summary type="html">&lt;p&gt;викификация, оформление&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''Язык ''C#''''' Как обой­ти его ог­ра­ни­че­ния при пе­ре­но­се ко­да, на­пи­сан­но­го на ''C++'' [[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
==''C#'' и ''Mono'': Сте­рео­эф­фект== &lt;br /&gt;
&lt;br /&gt;
: '''Ан­д­рей Кузь­мен­ко''' про­дол­жа­ет рас­сказ о пе­ре­но­се ко­да с ''C++'' на ''C#''. В этой ста­тье речь пой­дет о мно­же­ст­вен­ном на­сле­до­ва­нии.&lt;br /&gt;
&lt;br /&gt;
Сло­жи­лось так, что язык ''C#'' не под­дер­жи­ва­ет мно­же­ст­вен­ное на­сле­до­вание. Спо­ры о том, хо­ро­шо это или пло­хо, про­дол­жа­ют­ся уже дол­гое вре­мя, од­на­ко по боль­шей час­ти они принима­ют вид «ре­ли­ги­оз­ной вой­ны» и но­сят ско­рее тео­ре­ти­че­­ский ха­рак­тер. С дру­гой сто­ро­ны, при объ­ект­но-ори­ен­ти­ро­ван­ном под­хо­де к про­ек­ти­ро­ванию про­грамм­но­го обес­пе­чения мно­же­ст­вен­ное на­сле­до­вание час­то ока­зы­ва­ет­ся ме­ханиз­мом, наи­бо­лее аде­к­ват­но опи­сы­ваю­щим мо­де­ли­руе­мый объ­ект и его по­ве­дение. Кро­ме то­го, мо­жет возник­нуть необ­хо­ди­мость вы­полнить ре­ин­жиниринг су­ще­ст­вую­ще­го про­грамм­но­го обес­пе­чения: на­при­мер, пе­ренести код, на­пи­сан­ный на ''C++'' с ис­поль­зо­ванием мно­же­ст­вен­но­го на­сле­до­вания, на плат­фор­му ''Mono''. В дан­ной ста­тье бу­дет рас­смот­рен один из воз­мож­ных ва­ри­ан­тов под­хо­да к реа­ли­за­ции по­до­бия «на­стоя­ще­го» мно­же­ст­вен­но­го на­сле­до­вания в язы­ке ''C#''. Хо­чу сра­зу пре­ду­пре­дить чи­та­те­ля о том, что все те воз­мож­но­сти в том ви­де, как они есть в язы­ке ''C++'', в рам­ках плат­фор­мы ''Mono'' по­лу­чить нель­зя; од­на­ко мож­но про­вес­ти адап­та­цию имею­щих­ся язы­ко­вых средств и ис­поль­зо­вать ин­те­рес­ные и по­лез­ные прак­ти­че­­ские ре­зуль­та­ты для ре­шения оп­ре­де­лён­но­го клас­са за­дач – а имен­но, объ­е­динения раз­но­род­ных ие­рар­хий клас­сов.&lt;br /&gt;
&lt;br /&gt;
===Встать, суд идёт===&lt;br /&gt;
&lt;br /&gt;
Мно­же­ст­вен­ное на­сле­до­вание по­зво­ля­ет соз­дать про­из­вод­ный класс на осно­ве несколь­ких ба­зо­вых клас­сов. При этом кри­ти­ки тра­ди­ци­он­но ука­зы­ва­ют на две воз­мож­ные про­бле­мы. Пер­вая за­клю­ча­ет­ся в том, что по­яв­ля­ет­ся воз­мож­ность унас­ле­до­вать од­но и то­же же имя по­ля дан­ных или ме­то­да от несколь­ких ба­зо­вых клас­сов, что мо­жет по­слу­жить при­чи­ной не­од­но­знач­но­сти:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 //Ро­ди­тель­ские клас­сы&lt;br /&gt;
 class First { int function(int x); };&lt;br /&gt;
 class Second { int function(int x); };&lt;br /&gt;
 // До­чер­ний класс&lt;br /&gt;
 class Result : First, Second { }&lt;br /&gt;
 // соз­дан объ­ект клас­са&lt;br /&gt;
 Result res = new Result();&lt;br /&gt;
 // ка­кой имен­но ме­тод бу­дет вы­зван?&lt;br /&gt;
 res.function(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вто­рая про­бле­ма – это «ром­бо­вид­ное» на­сле­до­ва­ние, ко­гда в ие­рар­хии от ба­зо­во­го клас­са к про­из­вод­но­му су­ще­ст­ву­ет бо­лее од­но­го пу­ти. Вот клас­си­че­ский при­мер:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 class File{ string file_name};&lt;br /&gt;
 class InputFile : File {};&lt;br /&gt;
 class OutputFile : File {};&lt;br /&gt;
 class IOFile : InputFile, OutputFile {};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Возника­ет во­прос: долж­ны ли по­ля дан­ных ба­зо­во­го клас­са дуб­ли­ро­вать­ся в объ­ек­те под­клас­са столь­ко раз, сколь­ко су­ще­ст­ву­ет пу­тей ме­ж­ду ними в ие­рар­хии на­сле­до­вания? В язы­ках, под­дер­жи­ваю­щих мно­же­ст­вен­ное на­сле­до­вание, та­ких как ''С++, Eiffel, Python'', это ре­ша­ет­ся по-раз­но­му. &lt;br /&gt;
&lt;br /&gt;
Спра­вед­ли­во­сти ра­ди за­ме­чу, что спи­сок труд­но­стей, воз­ни­каю­щих при мно­же­ст­вен­ном на­сле­до­вании, не ис­чер­пы­ва­ет­ся те­ми дву­мя, что опи­са­ны вы­ше; од­на­ко су­ще­ст­ву­ют си­туа­ции, когда мно­же­ст­вен­ное на­сле­до­вание дей­ст­ви­тель­но оп­рав­да­но – на­при­мер, объ­е­динение неза­ви­си­мых ие­рар­хий клас­сов, ком­по­зи­ция ин­тер­фей­сов, соз­дание клас­са из ин­тер­фей­са и реа­ли­за­ции. На­ли­чие ме­ханиз­ма мно­же­ст­вен­но­го на­сле­до­вания де­ла­ет язык про­гра­ми­ро­вания вы­ра­зи­тельнее и бо­га­че, и наи­бо­лее спра­вед­ли­вый путь – это дать вы­бор про­грам­ми­сту: ис­поль­зо­вать имею­щие­ся воз­мож­но­сти или нет.&lt;br /&gt;
&lt;br /&gt;
===Тео­рия===&lt;br /&gt;
&lt;br /&gt;
Ес­ли класс рас­смат­ри­ва­ет­ся как ме­ханизм для пред­став­ления неко­то­рых сущ­но­стей, то ин­тер­фей­сы мож­но понимать как опи­сание неко­то­рых дей­ст­вий над эти­ми сущ­но­стя­ми. По су­ти сво­ей, ин­тер­фей­сы в язы­ке ''C#'' очень по­хо­жи на вир­ту­аль­ные ме­то­ды аб­ст­ракт­но­го клас­са в язы­ке ''C++''. Они опи­сы­ва­ют груп­пу свя­зан­ных функ­цио­наль­ных воз­мож­но­стей, ко­то­рые мо­гут при­над­ле­жать лю­бо­му клас­су и иметь ме­то­ды, свой­ст­ва, со­бы­тия, ин­дек­са­то­ры или лю­бое их со­че­тание. Ин­тер­фей­сы не мо­гут со­дер­жать по­ля дан­ных. Кро­ме то­го, они не со­дер­жат реа­ли­за­ции ме­то­дов. Когда го­во­рят, что класс на­сле­ду­ет ин­тер­фейс, это оз­на­ча­ет, что класс пре­достав­ля­ет реа­ли­за­цию для всех чле­нов, оп­ре­де­ляе­мых ин­тер­фей­сом. Та­ким об­ра­зом, в ин­тер­фей­се мы за­яв­ля­ем, что хо­тим сде­лать, но не оп­ре­де­ля­ем, как это бу­дет кон­крет­но реа­ли­зо­ва­но. Смысл в том, что для раз­ных клас­сов мы реа­ли­зо­вы­ва­ем од­но­тип­ный на­бор ме­то­дов с одним ви­дом их вы­зо­ва, но при этом реа­ли­за­ция ме­то­дов бу­дет раз­лич­аться в раз­ных клас­сах. До­пуска­ет­ся на­сле­до­вание про­из­воль­но­му чис­лу ин­тер­фей­сов.&lt;br /&gt;
&lt;br /&gt;
Су­ще­ст­вую­щая в язы­ке ''C#'' за­ме­на «пол­но­цен­но­го» мно­же­ст­вен­но­го на­сле­до­вания клас­сов на на­сле­до­вание ин­тер­фей­сов вы­гля­дит сле­дую­щим об­ра­зом: есть ба­зо­вый класс, ко­то­рый име­ет неко­то­рый на­бор дан­ных и ме­то­дов. Есть один или несколь­ко ин­тер­фей­сов, ко­то­рые пред­по­ла­га­ют вы­полнение клас­сом неко­то­рых опе­ра­ций. Про­из­вод­ный класс на­сле­ду­ет «пол­но­цен­но­му» клас­су, по­лу­чая от него неко­то­рые дан­ные и ме­то­ды, плюс принима­ет на се­бя обя­за­тель­ст­ва по реа­ли­за­ции тех ин­тер­фей­сов, ко­то­рым он на­сле­ду­ет.&lt;br /&gt;
&lt;br /&gt;
По­ня­тие ин­тер­фей­са лег­ко ил­лю­ст­ри­ру­ет­ся про­стым жи­тей­ским при­ме­ром. Пред­ставь­те се­бе мно­же­ст­во пред­ме­тов, от­но­ся­щих­ся к бы­то­вой элек­тронике: ви­део­магнито­фон, DVD-про­иг­ры­ва­тель, CD-плей­ер, ав­то­магнито­лу. Про­из­ве­дён­ные раз­ны­ми фир­ма­ми-из­го­то­ви­те­ля­ми и вы­пол­няю­щие раз­ные функ­ции, они все име­ют унифи­ци­ро­ван­ный поль­зо­ва­тель­ский ин­тер­фейс: ес­ли на­жать на кноп­ку с на­ри­со­ван­ным тре­угольником, уст­рой­ст­во бу­дет «иг­рать», а ес­ли на­жать кноп­ку с квад­ра­том, воспро­из­ве­дение пре­кра­тит­ся.&lt;br /&gt;
&lt;br /&gt;
===Прак­ти­ка===&lt;br /&gt;
&lt;br /&gt;
Да­вай­те рас­смот­рим про­грамм­ную мо­дель мо­биль­но­го те­ле­фо­на, ко­то­рый мо­жет ис­поль­зо­вать­ся как MP3‑плей­ер, для ра­бо­ты с ко­то­рым поль­зо­ва­те­лю доста­точ­но на­жи­мать со­от­ве­ст­вую­щие кноп­ки на кор­пу­се.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 using System;&lt;br /&gt;
 namespace Gadget&lt;br /&gt;
 {&lt;br /&gt;
 	 class MobilePhone&lt;br /&gt;
 	 {&lt;br /&gt;
 		 public void make_call(string number)&lt;br /&gt;
 {&lt;br /&gt;
 Console.WriteLine(“Call:” + number);&lt;br /&gt;
 }&lt;br /&gt;
 	 }&lt;br /&gt;
 	&lt;br /&gt;
 	 interface IManagement&lt;br /&gt;
 	 {&lt;br /&gt;
 		 void play( );&lt;br /&gt;
 		 void stop( );&lt;br /&gt;
 	 }&lt;br /&gt;
 	&lt;br /&gt;
 	 class Gadget : MobilePhone, IManagement&lt;br /&gt;
 	 {&lt;br /&gt;
 		 public void usb_connect( )&lt;br /&gt;
 {&lt;br /&gt;
 Console.WriteLine(“Connect to computer…”);&lt;br /&gt;
 }&lt;br /&gt;
 		 public void play( ) { Console.&lt;br /&gt;
 WriteLine(“Music playing now!”);}&lt;br /&gt;
 		 public void stop( ) { Console.WriteLine(“Stop playing music.”); }&lt;br /&gt;
 		&lt;br /&gt;
 	 }&lt;br /&gt;
 	&lt;br /&gt;
 	 class Program&lt;br /&gt;
 	 {&lt;br /&gt;
 		 public static void Main(string[] args)&lt;br /&gt;
 		 {&lt;br /&gt;
 			 Gadget g = new Gadget();&lt;br /&gt;
 			 g.play();&lt;br /&gt;
 			 g.stop();&lt;br /&gt;
 			 g.make_call(“89991234567”);&lt;br /&gt;
 		 }&lt;br /&gt;
 	 }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Страш­ная тай­на===&lt;br /&gt;
&lt;br /&gt;
В ли­те­ра­ту­ре, будь то учебники для на­чи­наю­щих или спра­вочники для про­фес­сио­на­лов, от­вет на во­прос о том, по­че­му же в язы­ке ''C#'' нет «нор­маль­но­го» мно­же­ст­вен­но­го на­сле­до­вания для клас­сов, за­час­тую очень рас­плыв­чат. Дес­кать, мно­же­ст­вен­ное на­сле­до­вание слож­но для понимания и яв­ля­ет­ся ис­точником по­тен­ци­аль­ных оши­бок. Я ду­маю, что сто­ит при­под­нять за­ве­су та­ин­ст­вен­но­сти и ра­зо­брать­ся в этой си­туа­ции.&lt;br /&gt;
&lt;br /&gt;
Вы­ше вы уже про­чли, что воз­мож­но унас­ле­до­вать од­но и то­ же имя по­ля дан­ных или ме­то­да от несколь­ких ба­зо­вых клас­сов. Как же тут быть? Язык ''C++'' пред­ла­га­ет такое ре­шение: ес­ли нет необ­хо­ди­мо­сти дуб­ли­ро­вать ин­фор­ма­цию в про­из­вод­ном клас­се, то все непо­сред­ст­вен­ные по­том­ки ба­зо­во­го клас­са в ром­бо­вид­ной схе­ме долж­ны на­сле­до­вать сво­ему пред­ку вир­ту­аль­но:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 class File{};&lt;br /&gt;
 class InputFile : public virtual File {};&lt;br /&gt;
 class OutputFile : public virtual File {};&lt;br /&gt;
 class IOFile : public InputFile, OutputFile {};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Та­ким об­ра­зом, '''File''' ста­но­вит­ся вир­ту­аль­ным ба­зо­вым клас­сом. Этот ва­ри­ант, бу­ду­чи про­стым и вполне по­нят­ным для про­грам­ми­ста, тре­бу­ет оп­ре­де­лён­ных «тру­до­за­трат» со сто­ро­ны ком­пи­ля­то­ра. Про­бле­ма в том, что вир­ту­аль­ные ба­зо­вые клас­сы реа­ли­зу­ют­ся как ука­за­те­ли. При этом раз­мер ко­да уве­ли­чи­ва­ет­ся, доступ к по­лям дан­ных вир­т­уаль­ных ба­зо­вых клас­сов ока­зы­ва­ет­ся мед­леннее по сравнению с невир­ту­аль­ны­ми ба­зо­вы­ми клас­са­ми, а сам ком­пи­ля­тор, ко­то­рый всё это реа­ли­зу­ет, ста­но­вит­ся «тя­же­лее». Важ­ное за­ме­чание: к мо­мен­ту оп­ре­де­ления клас­сов '''InputFile''' и '''OutputFile''' нет ника­кой ин­фор­ма­ции о том, бу­дет ли когда-нибудь ка­кой-ли­бо класс на­сле­до­вать от них обо­их или нет. Ес­ли из­на­чаль­но не объ­я­вить класс '''File''' вир­ту­аль­ным ба­зо­вым клас­сом, то мо­жет сло­жить­ся так, что раз­ра­бот­чи­ку клас­са '''IOFile''' по­тре­бу­ет­ся пе­ре­оп­ре­де­лить клас­сы '''InputFile''' и '''OutputFile''', а это бывает невоз­мож­ным по при­чине то­го, на­при­мер, что дан­ные клас­сы на­хо­дят­ся в ском­пи­ли­ро­ван­ной биб­лио­те­ке.&lt;br /&gt;
&lt;br /&gt;
При этом пра­ви­ла, со­глас­но ко­то­рым про­ис­хо­дит инициа­ли­за­ция вир­ту­аль­ных ба­зо­вых клас­сов, ока­зы­ва­ют­ся сложнее, чем в слу­чае «про­сто­го» на­сле­до­вания. При невир­ту­аль­ном на­сле­до­вании ар­гу­мен­ты кон­ст­рук­то­ра ба­зо­во­го клас­са за­да­ют­ся в спи­ске инициа­ли­за­ции непо­сред­ст­вен­но­го про­из­вод­но­го клас­са – та­ким об­ра­зом, ар­гу­мен­ты пе­ре­да­ют­ся со­вер­шен­но оче­вид­ным спо­со­бом: клас­сы уров­ня N транс­ли­ру­ют ар­гу­мен­ты вверх, клас­сам уров­ня (N–1). А в слу­чае вир­ту­аль­но­го на­сле­до­вания от­вет­ст­вен­ность за инициа­ли­за­цию ба­зо­во­го клас­са ло­жит­ся на са­мый по­следний до­черний класс в ие­рар­хии. По этой при­чине клас­сы, на­сле­дую­щие вир­ту­аль­но­му ба­зо­во­му и ну­ж­даю­щие­ся в инициа­ли­за­ции, долж­ны знать обо всех сво­их вир­ту­аль­ных пред­ках. Кро­ме то­го, при до­бав­лении в ие­рар­хию но­во­го про­из­вод­но­го клас­са он обя­зан при­нять на се­бя обя­за­тель­ст­во по инициа­ли­за­ции вир­ту­аль­ных ба­зо­вых клас­сов. Ко все­му про­че­му, возника­ет ре­зон­ный во­прос о спо­со­бах кор­рект­ной реа­ли­за­ции опе­ра­ций ко­пи­ро­вания и при­сваи­вания в све­те вы­ше­опи­сан­ных про­блем. Един­ст­вен­ный ра­дикаль­ный вы­ход в сло­жив­шей­ся си­туа­ции – это от­каз от по­лей дан­ных в ро­ди­тель­ских клас­сах. Та­ким об­ра­зом уст­ра­ня­ет­ся необ­хо­ди­мость в пе­ре­да­че ар­гу­мен­тов кон­ст­рук­то­рам вир­ту­аль­ных ба­зо­вых клас­сов.&lt;br /&gt;
&lt;br /&gt;
На­де­юсь, те­перь яс­но­сти ста­ло боль­ше. Же­лаю­щие ра­зо­брать­ся в этом во­про­се ещё глуб­же мо­гут це­ле­на­прав­лен­но про­дол­жить свои по­ис­ки, но пе­ред этим мы по­лу­чим мак­си­мум от воз­мож­но­стей плат­фор­мы ''Mono''. Как имен­но? Чи­та­ем даль­ше!&lt;br /&gt;
&lt;br /&gt;
===Что хо­тим===&lt;br /&gt;
&lt;br /&gt;
Ка­кую функ­цио­наль­ность нам бы хо­те­лось иметь, пусть да­же в пер­вом при­бли­жении? Итак:&lt;br /&gt;
* До­черний класс, на­сле­дуя сво­им ро­ди­те­лям, дол­жен иметь воз­мож­ность вы­зы­вать их ме­то­ды без необ­хо­ди­мо­сти для про­грам­ми­ста по­втор­но пе­ре­пи­сы­вать код. Сво­бо­да ис­поль­зо­вания то­го, что уже есть.&lt;br /&gt;
* Мы хо­тим иметь воз­мож­ность ис­поль­зо­вать объ­ек­ты до­чернего клас­са там, где ожи­да­ют­ся объ­ек­ты ро­ди­тель­ских клас­сов. Это по­зво­лит нам опе­ри­ро­вать про­из­вод­ным клас­сом че­рез ссыл­ки на ба­зо­вые клас­сы. По су­ти, обыч­ный по­ли­мор­физм.&lt;br /&gt;
* Ро­ди­тель­ские клас­сы для реа­ли­за­ции ме­ханиз­ма на­сле­до­вания не долж­ны под­вер­гать­ся ника­ким из­менениям. Дол­жен со­блю­дать­ся прин­цип це­ло­ст­но­сти. При со­блю­дении дан­но­го тре­бо­вания мы смо­жем ис­поль­зо­вать ро­ди­тель­ские клас­сы толь­ко по­сред­ст­вом их ин­тер­фей­са, что очень ак­ту­аль­но при ра­бо­те с ди­на­ми­че­­ски­­ми биб­лио­те­ка­ми, когда ис­ход­ный код недосту­пен.&lt;br /&gt;
&lt;br /&gt;
===Мо­де­ли­ро­вание===&lt;br /&gt;
&lt;br /&gt;
В ка­че­­ст­ве пред­мет­ной об­лас­ти мы бу­дем ис­поль­зо­вать офис­ную технику: прин­те­ры, сканеры и мно­го­функ­цио­наль­ные уст­рой­ст­ва (МФУ). Ду­маю, нет смыс­ла рас­ска­зы­вать что та­кое «прин­тер» и «сканер». Что ка­са­ет­ся МФУ, то это уст­рой­ст­ва, пре­ж­де все­го, объ­е­ди­няю­щие в се­бе воз­мож­но­сти прин­те­ра и сканера, так ска­зать, «два в од­ном». Кро­ме то­го, в неко­то­рых мо­де­лях мо­жет при­сут­ст­во­вать функ­цио­наль­ность фак­са и кард-ри­де­ра. МФУ по­зво­ля­ет эко­но­мить ме­сто на сто­ле и ин­тер­фейс­ные разъ­ё­мы, а ес­ли при­сут­ст­ву­ет под­держ­ка опе­ра­ци­он­ной сис­те­мы Linux, так и во­об­ще за­ме­ча­тель­ный ап­па­рат!&lt;br /&gt;
&lt;br /&gt;
Прин­те­ры и сканеры в на­шем слу­чае сим­во­ли­зи­ру­ют две неза­ви­си­мые ие­рар­хии, ко­то­рые мы хо­тим объ­е­динить с це­лью по­лу­чения но­вой сущ­но­сти – МФУ. Оче­вид­но, что класс прин­те­ров об­ла­да­ет свои­ми спе­ци­фи­че­­ски­­ми дан­ны­ми и ме­то­да­ми, а класс сканеров – свои­ми. Класс МФУ, на­сле­дуя клас­су «прин­тер» и клас­су «сканер», по­лу­чит оп­ре­де­лён­ный на­бор ме­то­дов от ро­ди­тель­ских клас­сов, и кро­ме то­го, у него бу­дут свои соб­ст­вен­ные по­ля дан­ные и ме­то­ды.&lt;br /&gt;
&lt;br /&gt;
Для про­грамм­ной мо­де­ли из все­го мно­го­об­ра­зия свойств прин­те­ров мы бу­дем ис­поль­зо­вать свой­ст­во «язык опи­сания стра-ниц» (Page Description Language – PDL), а сканер бу­дет оп­ре­де­лять­ся оп­ти­че­­ским раз­ре­шением. В при­ла­гае­мом фай­ле '''LibDev.cs''' со­дер­жит­ся код клас­сов '''Printer''' и '''Scaner'''. Ти­пы дан­ных, пред­став­ляю­щие со­бой ро­ди­тель­ские клас­сы '''Printer''' и '''Scaner''', очень про­сты. Они вынесе­ны в своё соб­ст­вен­ное про­стран­ст­во имён '''OfficeDevices'''. Дан­ный файл ком­пи­ли­ру­ет­ся в '''dll'''-биб­лио­те­ку, ко­то­рая за­тем ис­поль­зу­ет­ся в основ­ном про­ек­те.&lt;br /&gt;
&lt;br /&gt;
Тип да­нных «'''ПРИНТЕР'''» со­дер­жит по­ле дан­ных '''page_language''', свой­ст­во '''Page_Language''' с про­це­ду­ра­ми досту­па '''get''' и '''set''', кон­ст­рук­тор с па­ра­мет­ром для инициа­ли­за­ции по­ля дан­ных и ме­тод '''ShowPageLanguageInfo()''', вы­во­дящий на кон­соль зна­чение по­ля дан­ных. Тип дан­ных «'''СКАНЕР'''» – по­ле дан­ных '''resolution''', свой­ст­во '''Resolution''' с про­це­ду­ра­ми досту­па '''get''' и '''set''', кон­ст­рук­тор с па­ра­мет­ром для инициа­ли­за­ции по­ля дан­ных и ме­тод '''ShowResolutionInfo()''', вы­во­дящий на кон­соль зна­чение по­ля дан­ных.&lt;br /&gt;
&lt;br /&gt;
Класс-на­следник – '''MFU''' (сущ­ность «'''МНОГОФУНКЦИОНАЛЬНОЕ УСТРОЙСТВО'''»). Имен­но он яв­ля­ет­ся на­шей конеч­ной це­лью.&lt;br /&gt;
&lt;br /&gt;
===Язы­ко­вые сред­ст­ва===&lt;br /&gt;
&lt;br /&gt;
Ком­по­зи­ция – это от­но­шение ме­ж­ду клас­са­ми, возникаю­щее тогда, когда объ­ект од­но­го ти­па со­дер­жит в се­бе объ­ек­ты дру­гих ти­пов. В ка­че­­ст­ве си­нонимов тер­ми­на «ком­по­зи­ция» вы­сту­па­ют «вло­жение» или «аг­ре­ги­ро­вание». При­мер:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 class Address {}&lt;br /&gt;
 class ContactInfo{}&lt;br /&gt;
 class Person&lt;br /&gt;
 {&lt;br /&gt;
 	 Address address;&lt;br /&gt;
 	 ContactInfo contact;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Когда мы го­во­рим о на­сле­до­вании, то для нас это оз­на­ча­ет, что «класс яв­ля­ет­ся раз­но­вид­но­стью дру­го­го клас­са». В тер­мин «ком­по­зи­ция» вкла­ды­ва­ет­ся смысл «со­дер­жит» или «реа­ли­зу­ет­ся по­сред­ст­вом».&lt;br /&gt;
&lt;br /&gt;
Бы­ва­ет так, что при раз­ра­бот­ке про­грамм тре­бу­ет­ся вы­полнить при­ве­дение од­но­го соз­дан­но­го про­грам­ми­стом ти­па дан­ных к дру­го­му. Для ре­шения та­кой за­да­чи в язы­ке ''C#'' пре­ду­смот­ре­ны сред­ст­ва яв­но­го и неяв­но­го пре­об­ра­зо­вания ти­пов. Ес­ли в про­грам­ме есть класс '''First''' и класс '''Second''' и нам нуж­но вы­пол­нять неяв­ное пре­об­ра­зо­вание ти­па '''Second''' к ти­пу '''First''', то в клас­се '''First''' нуж­но объ­я­вить ме­тод при­ве­де­ния сле­дую­ще­го ви­да:&lt;br /&gt;
&lt;br /&gt;
 public static implicit operator First(Second s) { // не­об­хо­ди­мые дей­ст­вия}.&lt;br /&gt;
&lt;br /&gt;
Фак­ти­че­ски, вы­пол­ня­ет­ся пе­ре­груз­ка опе­ра­то­ра.&lt;br /&gt;
&lt;br /&gt;
===Ко­ди­ро­ва­ние===&lt;br /&gt;
&lt;br /&gt;
Шаг пер­вый – соз­да­ние клас­сов-на­след­ни­ков '''Scaner_tmp''' и '''Printer_tmp'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 public class Printer_tmp : Printer&lt;br /&gt;
 {&lt;br /&gt;
 	 internal MFU mfu_part;&lt;br /&gt;
    public Printer_tmp(string data) : base(data) { }&lt;br /&gt;
    static public implicit operator MFU(Printer_tmp p)&lt;br /&gt;
    {&lt;br /&gt;
 	 return p.mfu_part;&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
 	&lt;br /&gt;
 public class Scaner_tmp : Scaner&lt;br /&gt;
 {&lt;br /&gt;
 internal MFU mfu_part;&lt;br /&gt;
    public Scaner_tmp(string data) : base(data) { }&lt;br /&gt;
    static public implicit operator MFU(Scaner_tmp s)&lt;br /&gt;
    {&lt;br /&gt;
         return s.mfu_part;&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Бла­го­да­ря на­сле­до­ванию клас­сов '''Printer''' и '''Scaner''' че­рез них мож­но по­лу­чить доступ к функ­ци­ям '''ShowPageLanguageInfo()''' и '''ShowResolutionInfo()''', а так­же к свой­ст­вам ро­ди­тель­ских клас­сов. Мы ви­дим, что ка­ж­дый из этих клас­сов-на­следников име­ет по­ле дан­ных '''mfu_part''' и опе­ра­тор для при­ве­дения ти­па, ко­то­рый по­зво­ля­ет пре­об­ра­зо­вать эти клас­сы к ти­пу '''MFU'''. За­чем это нуж­но? Для то­го, что­бы мог вы­пол­нять­ся сле­дую­щий код:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 Printer p1 = new Printer(“PostScript”);&lt;br /&gt;
 Printer p2 = new Printer(“PCL6”);&lt;br /&gt;
 MFU p3 = new MFU(“PCL5”, “1200x1200”, “USB 1.1”);&lt;br /&gt;
&lt;br /&gt;
 Printer [] arr = {p1, p2, p3};&lt;br /&gt;
 foreach (Printer print_i in arr) print_i.ShowPageLanguageInfo();&lt;br /&gt;
&lt;br /&gt;
 Printer_tmp tmp = (Printer_tmp)arr[2];&lt;br /&gt;
 MFU mfu = (MFU)tmp;&lt;br /&gt;
 mfu.Mfu_SelfTest();&lt;br /&gt;
 mfu.ShowPageLanguageInfo();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В мас­си­ве '''arr''' ти­па '''Printer''' со­дер­жит­ся три эле­мен­та: два из них – это ука­за­те­ли на «на­стоя­щие» прин­те­ры, а тре­тий эле­мент – ука­за­тель на объ­ект клас­са '''MFU''', ра­бо­та с ко­то­рым ве­дёт­ся че­рез ука­за­тель на ро­ди­тель­ский класс '''Printer'''. В цик­ле '''foreach''' все эле­мен­ты мас­си­ва «счи­та­ют­ся» прин­те­ра­ми, од­на­ко в ка­кой-то мо­мент вре­мени нам мо­жет по­на­до­бить­ся ра­бо­тать с тре­тим эле­мен­том имен­но как с объ­ек­том клас­са '''MFU''', ко­им он, соб­ст­вен­но, и яв­ля­ет­ся.&lt;br /&gt;
&lt;br /&gt;
Шаг вто­рой: соз­да­ние до­чер­не­го клас­са '''MFU'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
 public class MFU&lt;br /&gt;
 {&lt;br /&gt;
 Printer_tmp printer_part;&lt;br /&gt;
     Scaner_tmp scaner_part;&lt;br /&gt;
     string interface_type;&lt;br /&gt;
     public string Interface&lt;br /&gt;
     {&lt;br /&gt;
             get&lt;br /&gt;
         {&lt;br /&gt;
            return interface_type;&lt;br /&gt;
         }&lt;br /&gt;
         set&lt;br /&gt;
         {&lt;br /&gt;
            interface_type = value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
             public string Language&lt;br /&gt;
     {&lt;br /&gt;
             get&lt;br /&gt;
         {&lt;br /&gt;
            return printer_part.Page_Language;&lt;br /&gt;
         }&lt;br /&gt;
         set&lt;br /&gt;
         {&lt;br /&gt;
            printer_part.Page_Language = value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     public string Resolution&lt;br /&gt;
     {&lt;br /&gt;
         get&lt;br /&gt;
         {&lt;br /&gt;
            return scaner_part.Resolution;&lt;br /&gt;
         }&lt;br /&gt;
         set&lt;br /&gt;
         {&lt;br /&gt;
            scaner_part.Resolution = value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     public MFU(string lang, string res, string iface)&lt;br /&gt;
     {&lt;br /&gt;
             printer_part = new Printer_tmp(lang);&lt;br /&gt;
         scaner_part = new Scaner_tmp(res);&lt;br /&gt;
         printer_part.mfu_part = this;&lt;br /&gt;
         scaner_part.mfu_part = this;&lt;br /&gt;
         interface_type = iface;&lt;br /&gt;
     }&lt;br /&gt;
     public void ShowPageLanguageInfo()&lt;br /&gt;
     {&lt;br /&gt;
         printer_part.ShowPageLanguageInfo( );&lt;br /&gt;
     }&lt;br /&gt;
     public void ShowResolutionInfo()&lt;br /&gt;
     {&lt;br /&gt;
&lt;br /&gt;
             scaner_part.ShowResolutionInfo( );&lt;br /&gt;
     }&lt;br /&gt;
     public void Mfu_SelfTest()&lt;br /&gt;
     {&lt;br /&gt;
                        Console.WriteLine(“MFU ready to work!”);&lt;br /&gt;
     }&lt;br /&gt;
     static public implicit operator Scaner(MFU mfu)&lt;br /&gt;
     {&lt;br /&gt;
         return mfu.scaner_part;&lt;br /&gt;
     }&lt;br /&gt;
     static public implicit operator Printer(MFU mfu)&lt;br /&gt;
     {&lt;br /&gt;
         return mfu.printer_part;&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
По­ля дан­ных: '''interface_type''' – спо­соб под­клю­чения уст­рой­ст­ва к ком­пь­ю­те­ру. Да­лее идут '''printer_part''' и '''scaner_part''' – это то са­мое аг­ре­ги­ро­вание, ко­то­рое по­мо­жет нам сде­лать по­до­бие мно­же­ст­вен­но­го на­сле­до­вания. Ин­тер­фей­сы для ра­бо­ты с по­ля­ми дан­ных клас­са: '''Interface, Language''' и '''Resolution'''. Ме­то­ды: '''ShowPageLanguageInfo()''' и '''ShowResolutionInfo()''' ор­ганизу­ют доступ к ме­то­дам ро­ди­тель­ских клас­сов. Опе­ра­то­ры пре­об­ра­зо­вания ти­пов при­во­дят тип '''MFU''' к ти­пам '''Scaner''' и '''Printer'''. Имен­но бла­го­да­ря им станет воз­мо­жен код ви­да:&lt;br /&gt;
&lt;br /&gt;
 Printer p = new MFU(“PostScript”, “600x600”, “LAN”);&lt;br /&gt;
 Scaner s = new MFU(“PCL6”, “1200X1200”, “USB 2.0”);&lt;br /&gt;
&lt;br /&gt;
Те­перь внима­тель­но рас­смот­рим кон­ст­рук­тор клас­са '''MFU'''. Для «свя­зи с пред­ка­ми», т. е. реа­ли­за­ции стра­те­гии аг­ре­ги­ро­вания, соз­да­ют­ся объ­ек­ты '''Printer_tmp''' и '''Scaner_tmp'''. С их по­мо­щью мы по­лу­чим доступ к функ­ци­ям '''ShowPageLanguageInfo()''' и '''ShowResolutionInfo()''', на­хо­дя­щим­ся в клас­сах '''Printer''' и '''Scaner'''.&lt;br /&gt;
&lt;br /&gt;
За­тем у соз­дан­ных объ­ек­тов инициа­ли­зи­ру­ют­ся по­ля '''mfu_part''', ссыл­кой на соз­да­вае­мый объ­ект ти­па '''MFU'''. Это бу­дет ис­поль­зо­вать­ся при опе­ра­ци­ях при­ве­дения ти­па.&lt;br /&gt;
&lt;br /&gt;
На­конец, инициа­ли­зи­ру­ет­ся по­ле дан­ных '''interface_type'''. По­смот­рим, ка­к мож­но ис­поль­зо­вать по­лу­чен­ный код:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&lt;br /&gt;
 static void Main(string[] args)&lt;br /&gt;
 { &lt;br /&gt;
 	 Printer printer1 = new Printer(“PostScript”);&lt;br /&gt;
 Printer printer2 = new Printer(“SPL”);&lt;br /&gt;
 MFU printer3 = new MFU(“PCL6”, “1200x1200”, “USB 2.0”);&lt;br /&gt;
&lt;br /&gt;
 List&amp;lt;Printer&amp;gt; data = new List&amp;lt;Printer&amp;gt;();&lt;br /&gt;
 data.Add(printer1);&lt;br /&gt;
 data.Add(printer2);&lt;br /&gt;
 data.Add(printer3);&lt;br /&gt;
&lt;br /&gt;
 foreach (Printer print_i in data)&lt;br /&gt;
   print_i.ShowPageLanguageInfo();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 Вы­вод на кон­соль:&lt;br /&gt;
 Page language:PostScript&lt;br /&gt;
 Page language:SPL&lt;br /&gt;
 Page language:PCL6&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На этом всё. Же­лаю ус­пеш­ной ра­бо­ты!&lt;br /&gt;
&lt;br /&gt;
===''Mono'': Под­клю­ча­ем биб­лио­те­ки===&lt;br /&gt;
&lt;br /&gt;
Пусть наш файл с име­нем '''ProgramLib.cs''' со­дер­жит не­кий ис­ход­ный код на язы­ке ''C#'', ко­то­рый мы хо­тим пре­вра­тить в ди­на­ми­че­ски под­клю­чае­мую биб­лио­те­ку. Файл '''Program.cs''' со­дер­жит код про­грам­мы, ко­то­рый в сво­ей ра­бо­те ис­поль­зу­ет код биб­лио­те­ки '''ProgramLib.dll'''. На­би­ра­ем в кон­со­ли сле­дую­щие ко­ман­ды:&lt;br /&gt;
&lt;br /&gt;
 $ gmcs -t:library ProgramLib.cs&lt;br /&gt;
 $ gmcs -r:ProgramLib.dll Program.cs&lt;br /&gt;
&lt;br /&gt;
Вот та­ким не­хит­рым об­ра­зом обес­пе­чи­ва­ет­ся ра­бо­та с ди­на­ми­че­ски­ми биб­лио­те­ка­ми в ко­манд­ной стро­ке.&lt;/div&gt;</summary>
		<author><name>Crazy Rebel</name></author>	</entry>

	</feed>