<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://zine.net.pl/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">In the .Net</title><subtitle type="html">O programowaniu w .Net i tworzeniu oprogramowania w ogólności</subtitle><id>http://zine.net.pl/blogs/chaniewski/atom.aspx</id><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/chaniewski/default.aspx" /><link rel="self" type="application/atom+xml" href="http://zine.net.pl/blogs/chaniewski/atom.aspx" /><generator uri="http://communityserver.org" version="2.1.61129.2">Community Server</generator><updated>2008-08-27T21:43:00Z</updated><entry><title>Najszybszy(?) sposób na aktualizację UI z innego wątku</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/chaniewski/archive/2008/08/27/najszybszy-spos-b-na-aktualizacj-ui-z-innego-w-tku.aspx" /><id>http://zine.net.pl/blogs/chaniewski/archive/2008/08/27/najszybszy-spos-b-na-aktualizacj-ui-z-innego-w-tku.aspx</id><published>2008-08-27T21:53:00Z</published><updated>2008-08-27T21:53:00Z</updated><content type="html">
&lt;p&gt;... czyli Kochamy Delegaty, Wyrażenia Lambda i Extension Methods :) &lt;br&gt;&lt;/p&gt;

&lt;p&gt;Pisząc aplikacje Windows nie raz i nie dwa zdarza się, że musimy zaktualizować zawartość interfejsu użytkownika z poziomu innego wątku (który np. wykonuje jakieś obliczenia lub wykonuje długotrwałą operację). Niestety nie możemy tego zrobić bezpośrednio przez ustawienie odpowiednich właściwości w kodzie. Spróbujmy w oddzielnym wątku ustawić tytuł formularza:&lt;br&gt;&lt;/p&gt;


&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; button1_Click(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    var asyncCode = &lt;span class="kwrd"&gt;new&lt;/span&gt; Thread(() =&amp;gt; Text = &lt;span class="str"&gt;"Ala ma kota"&lt;/span&gt;) { IsBackground = &lt;span class="kwrd"&gt;true&lt;/span&gt; };&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    asyncCode.Start();&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;W efekcie otrzymamy elegancki wyjątek:&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on. &lt;/i&gt;&lt;/p&gt;

&lt;p&gt;(dodatkowo widać, jak wyrażenia lambda upraszczają nasz kod - w tym przypadku wykonanie operacji asynchronicznie)&lt;/p&gt;

&lt;p&gt;Cóż, trzeba to jakoś naprawić. W tym celu zrobimy sobie Extension Method, którego będziemy mogli używać na wszystkich obiektach dziedziczących z klasy &lt;code&gt;Form&lt;/code&gt;.&lt;/p&gt;


&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;delegate&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; AsyncMethodInvoker();&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt; &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; FormAsyncUpdateExtension&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; AsyncUpdate(&lt;span class="kwrd"&gt;this&lt;/span&gt; Form form, AsyncMethodInvoker asyncDelegate)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt;(form.InvokeRequired)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;            form.Invoke(asyncDelegate);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;            asyncDelegate();&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

W linii pierwszej definiujemy delegata, który nam się dalej przyda (wiem wiem, mamy we Frameworku gotowy identyczny delegat &lt;code&gt;MethodInvoker&lt;/code&gt;, ale przykładów nigdy za wiele). A zaraz potem używamy go do zdefiniowania metody rozszerzającej klasę &lt;code&gt;Form&lt;/code&gt;.
Od tej pory będziemy mogli pisać kod typu: &lt;code&gt;moj_formularz.AsyncUpdate(() =&amp;gt; moj_formularz.Text = "Ala ma kota");&lt;/code&gt;. Sprawdźmy:

&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; button1_Click(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    var asyncCode = &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;        &lt;span class="kwrd"&gt;new&lt;/span&gt; Thread(() =&amp;gt; &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;            &lt;span class="kwrd"&gt;this&lt;/span&gt;.AsyncUpdate(() =&amp;gt; Text = &lt;span class="str"&gt;"Ala ma kota"&lt;/span&gt;)) &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;            { IsBackground = &lt;span class="kwrd"&gt;true&lt;/span&gt; };&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    asyncCode.Start();&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Działa! Czy ktoś zna metodę, która po napisaniu raz, jest potem jeszcze zgrabniejsza w użyciu?&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=1831" width="1" height="1"&gt;</content><author><name>chaniewski</name><uri>http://zine.net.pl/members/chaniewski.aspx</uri></author><category term=".Net_Framework c#" scheme="http://zine.net.pl/blogs/chaniewski/archive/tags/.Net_5F00_Framework+c_2300_/default.aspx" /></entry><entry><title>Wyszukiwanie plug-inów z szacunkiem dla pamięci</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/chaniewski/archive/2008/08/27/wyszukiwanie-plug-in-w-z-szacunkiem-dla-pami-ci.aspx" /><id>http://zine.net.pl/blogs/chaniewski/archive/2008/08/27/wyszukiwanie-plug-in-w-z-szacunkiem-dla-pami-ci.aspx</id><published>2008-08-27T19:43:00Z</published><updated>2008-08-27T19:43:00Z</updated><content type="html">&lt;p&gt;Bardzo często, gdy tworzymy nietrywialną aplikację, pojawia się konieczność rozszerzania jej za pomocą plug-inów, czy też mówiąc po naszemu po prostu wtyczek. W tej notce nie będę skupiał się na tym jak tworzyć mechanizmy rozszerzalności czy też jak zaprojektować architekturę aplikacji, by udostępniała taką funkcjonalność. Zamiast tego, założę że te fragmenty mamy już gotowe i chcemy po prostu wyszukać na dysku pliki (assemblies) zawierające wtyczki - a także wyciągnąć z tych wtyczek pewne informacje.&lt;/p&gt;

&lt;p&gt;Temat w zasadzie nie jest skomplikowany - &lt;code&gt;Assembly.Load()&lt;/code&gt;, do tego trochę refleksji, i już. Co się jednak dzieje, jeżeli na dysku mamy więcej assemblies? Co, jeżeli jedynie niektóre wtyczki używamy - bo aplikacja pozwala w innym miejscu je wyłączyć, albo steruje ich użyciem innego rodzaju logika?&lt;/p&gt;

&lt;p&gt;Zwykłe &lt;code&gt;Assembly.Load()&lt;/code&gt; jest nieodwołalne. Kod załadowany do bieżącej domeny aplikacji pozostanie tam do czasu, gdy zamkniemy aplikację. Jeżeli przeglądaliśmy w poszukiwaniu wtyczek wiele bibliotek, to może się okazać, że w pamięci trzymamy znacznie więcej śmiecia niż jest to nam konieczne.&lt;/p&gt;

&lt;p&gt;W poprzednim akapicie kluczowym fragmentem było "załadowany do &lt;b&gt;bieżącej&lt;/b&gt; domeny aplikacji". Rozwiązaniem naszego problemu jest utworzenie domeny tymczasowej, wywołanie w niej kodu wykrywającego pluginy, a następnie usunięcie całej tej domeny z pamięci. Ale niestety, życie nie jest tak proste jak mogłoby się wydawać...&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Pierwsze podejście&lt;/b&gt;&lt;/p&gt;



&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;AppDomain temporary = AppDomain.CreateDomain(&lt;span class="str"&gt;"TEMPORARY"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;Assembly a = temporary.Load(&lt;span class="str"&gt;"testowane_assembly.dll"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="rem"&gt;// wyszukanie pluginów wewnątrz załadowanego assembly&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="rem"&gt;// zapewne korzystające z a.GetExportedTypes()&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;AppDomain.Unload(temporary);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Niestety, powyższy kod, choć na pierwszy rzut oka mógłby wydawać się poprawny, nie zadziała jak trzeba. Owszem, załaduje assembly z dysku. Owszem, wyszuka w nim wtyczki. Owszem, usunie tymczasową domenę aplikacji... ale wywołanie &lt;code&gt;AppDomain.CurrentDomain.GetAssemblies()&lt;/code&gt; pokaże, że w bieżącej, głównej domenie naszej aplikacji, ciągle tkwi załadowane assembly które przeszukiwaliśmy... i którego się w tym miejscu w żaden sposób nie spodziewaliśmy.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Dlaczego tak się dzieje? Co sprowadziło nasze assembly na złą drogę?&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Dzieje się tak dlatego, że klasa &lt;code&gt;Assembly&lt;/code&gt; nie dziedziczy z &lt;code&gt;MarshalByRefObject&lt;/code&gt;. W praktyce oznacza to, że efekt wywołania metody &lt;code&gt;temporary.Load(...)&lt;/code&gt;, czyli właśnie obiekt klasy Assembly, nie może być używany "zdalnie" (marshalowany? jak to po polsku się mówi?) pomiędzy dwiema domenami aplikacji. W rezultacie, nasza biblioteka zawierająca (lub nie) wtyczkę jest ładowana również do podstawowej domeny aplikacji. A to pech.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Podejście drugie, właściwe&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Cóż, jeżeli jedynym problemem powodującym wyciekanie assembly do naszej podstawowej domeny aplikacji jest fakt, że klasa &lt;code&gt;Assembly&lt;/code&gt; nie dziedziczy z &lt;code&gt;MarshalByRefObject&lt;/code&gt;... to zróbmy sobie przejściówkę, która się może takim przodkiem poszczycić. I niech ta przejściówka po cichu zrobi co ma zrobić, tak by podstawowa domena aplikacji o tym nie wiedziała, a potem zwróci jedynie rezultat.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Spróbujmy zatem w ten sposób.&lt;/p&gt;

&lt;p&gt;Zróbmy sobie malutki dodatkowy projekcik, niech nazywa się on &lt;code&gt;Proxy&lt;/code&gt;, zawierający klasę:&lt;/p&gt;


&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Gateway : MarshalByRefObject&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; RezultatSzukania LoadAndMatch(&lt;span class="kwrd"&gt;string&lt;/span&gt; fileName)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;        &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;            &lt;span class="rem"&gt;// załadowanie assembly&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;            Assembly pluginAssembly = Assembly.LoadFile(fileName);&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;            &lt;span class="rem"&gt;// wyszukanie wtyczek...&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Type type &lt;span class="kwrd"&gt;in&lt;/span&gt; pluginAssembly.GetExportedTypes())&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;            {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;                &lt;span class="rem"&gt;// ...&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;            }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;        &lt;span class="kwrd"&gt;catch&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;            &lt;span class="rem"&gt;// ... tu paskudnie połykam wyjątki :p&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; rezultatSzukania;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Pozostaje teraz tylko skorzystać z gotowej przelotki:&lt;/p&gt;


&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;AppDomain temporary = AppDomain.CreateDomain(&lt;span class="str"&gt;"TEMPORARY"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    Gateway proxy =&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;                    temporary.CreateInstanceAndUnwrap(&lt;span class="str"&gt;"Proxy"&lt;/span&gt;, &lt;span class="str"&gt;"Proxy.Gateway"&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;                    Gateway;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    RezultatSzukania rezultat = proxy.LoadAndMatch(&lt;span class="str"&gt;"testowane_assembly.dll"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;}&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&lt;span class="kwrd"&gt;finally&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;    AppDomain.Unload(temporary);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Co tu się dzieje? No cóż, to dosyć proste. Po utworzeniu tymczasowej domeny aplikacji, używamy na tej domenie metody &lt;code&gt;CreateInstanceAndUnwrap()&lt;/code&gt;, która ładuje assembly &lt;code&gt;Proxy.dll&lt;/code&gt; do tymczasowej domeny, tworzy obiekt klasy &lt;code&gt;Gateway&lt;/code&gt; - również w tej domenie, a następnie tworzy &lt;span style="font-style:italic;"&gt;proxy&lt;/span&gt; do tego obiektu w bieżącej domenie. Zupełnie jak w remotingu - aby to sobie unaocznić, najlepiej na chwilę sobie wyobrazić, że tymczasowa domena powstała na innym komputerze, w innej galaktyce... więc używamy lokalnego obiektu &lt;span style="font-style:italic;"&gt;proxy&lt;/span&gt; reprezentującego kod działający zdalnie. Gdy wywołamy metodę &lt;code&gt;LoadAndMatch&lt;/code&gt; na lokalnym obiekcie &lt;span style="font-style:italic;"&gt;proxy&lt;/span&gt;, tak naprawdę jej argumenty będą przesłane (przemarshalowane, blah) do metody na obiekcie w tymczasowej domenie - a rezultat zostanie przesłany z powrotem do &lt;i&gt;proxy&lt;/i&gt; i do głównej domeny aplikacji.&lt;/p&gt;

&lt;p&gt;Kilka uwag: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RezultatSzukania&lt;/code&gt; musi być serializowalny (jest to konieczne, aby można było przekazać go między domenami aplikacji). &lt;br&gt;&lt;/li&gt;

&lt;li&gt;Definicję tej klasy należy umieścić ponadto w takim miejscu, aby była widoczna zarówno dla obiektu &lt;code&gt;Gateway&lt;/code&gt; w assembly &lt;code&gt;Proxy&lt;/code&gt;, jak i dla naszej głównej aplikacji.&lt;/li&gt;

&lt;li&gt;Assembly &lt;code&gt;Proxy&lt;/code&gt; przecieka do naszej głównej domeny aplikacji. Jeżeli chce się tego uniknąć, to trzeba wyciągnąć z obiektu &lt;code&gt;Gateway&lt;/code&gt; interfejs (np. &lt;code&gt;IGateway&lt;/code&gt;), umieścić go w miejscu widocznym zarówno dla assembly &lt;code&gt;Proxy&lt;/code&gt; jak i głównej aplikacji, usunąć referencję do assembly &lt;code&gt;Proxy&lt;/code&gt; z aplikacji - a przy wywołaniu &lt;code&gt;CreateInstanceAndUnwrap()&lt;/code&gt; rezultat rzutować na &lt;code&gt;IGateway&lt;/code&gt; zamiast na &lt;code&gt;Gateway&lt;/code&gt;.&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=1830" width="1" height="1"&gt;</content><author><name>chaniewski</name><uri>http://zine.net.pl/members/chaniewski.aspx</uri></author><category term=".Net_Framework" scheme="http://zine.net.pl/blogs/chaniewski/archive/tags/.Net_5F00_Framework/default.aspx" /></entry></feed>