﻿<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel><title>Blogi Cezarego Walenciuka</title>
<description>Tutaj piszę o przemawianiu i programowaniu</description>
<generator>Miniblog.Core</generator>
<link>https://cezarywalenciuk.pl/</link>
<item>
  <title>Asynchroniczny C# : IAsyncEnumerable</title>
  <link>https://cezarywalenciuk.pl/blog/programing/iasyncenumerable-w-c</link>
  <description>&lt;p&gt;[metro]LINQ i IEnumerable&amp;lt;T&amp;gt;. LINQ od .NET 3.5 daje nam wiele możliwości do modyfikowania, filtrowania sekwencji danych.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Co, jednak jeśli tw&amp;oacute;j kod pracuje asynchronicznie. Jak wtedy ustawić te rury LINQ, aby prze-mapować dane lub je przefiltrować.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Z .NET 6 pojawiła się nowy interfejs "IAsyncEnumerable&amp;lt;T&amp;gt;", kt&amp;oacute;re zostały stworzone z myślą o tak zwanych asynchronicznych strumieniach".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;W esencji "IAsyncEnumerable&amp;lt;T&amp;gt;&amp;rdquo; działa tak samo, jak&amp;nbsp; IEnumerable&amp;lt;T&amp;gt; tylko każde elementy takiej strumieniowanej kolekcji wychodzą asynchronicznie. Co nie blokuje programu, gdy wyciągniecie każdego elementu ze strumienia może nawet trwać na przykład ponad sekundę.&lt;/p&gt;
&lt;p&gt;Postanowiłem odświeżyć wątek asynchronicznego C# ponieważ wierz mi mamy jeszcze wiele temat&amp;oacute;w do om&amp;oacute;wienia.&amp;nbsp; Zobaczmy co potrafi IAsyncEnumerable&amp;lt;T&amp;gt;.&lt;/p&gt;
&lt;p&gt;[more]&lt;/p&gt;
&lt;h3&gt;Dlaczego IAsyncEnumerable, a nie Task&amp;lt;IEnumerable&amp;lt;T&amp;gt;&amp;gt;&lt;/h3&gt;
&lt;p&gt;Wr&amp;oacute;ćmy do podstaw IEnumerable&amp;lt;T&amp;gt;. Gdy jakaś metoda zwraca IEnumerable&amp;lt;T&amp;gt; to znaczy, że dana sekwencja element&amp;oacute;w nie została jeszcze wygenerowana.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Pętla foreach wyciągnie pojedyncze element z tego interfejsu, a operacja "Count()" wymusi przetworzenia całej kolekcji, gdyż inaczej nie można poznać całkowitej liczby element&amp;oacute;w w takiej leniwej kolekcji.&lt;/p&gt;
&lt;p&gt;Dodatkowo IEnumerable&amp;lt;T&amp;gt; daje możliwość tworzenia swoich kolekcji, kt&amp;oacute;re są wirtualne albo i nie.&lt;/p&gt;
&lt;p&gt;Tutaj przykładowo opakowuje tylko listę element&amp;oacute;w.&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;class MyObjects : IEnumerable&amp;lt;MyObject&amp;gt;
{
    List&amp;lt;MyObject&amp;gt; mylist = new List&amp;lt;MyObject&amp;gt;();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator&amp;lt;MyObject&amp;gt; GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Przy użyciu słowa kluczowego yield można tworzyć coś, co ja nazywam wirtualnymi kolekcjami.&lt;/p&gt;
&lt;p&gt;Kod pochodzi z &lt;a title="yield słowo kluczowe Ułatwienie implementacji IEnumerable" href="https://cezarywalenciuk.pl/blog/programing/yield-sowo-kluczowe--uatwienie-implementacji-ienumerablelttgt" target="_blank" rel="noopener"&gt;mojego starego wpisu.&lt;/a&gt;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;static public IEnumerable&amp;lt;string&amp;gt; Napisy()
{
    yield return "Ala";
    yield return "ma";
    yield return "radioaktywnego";
    yield return "kota";
    yield return "z";
    yield return "Marsa";
}

static void Main(string[] args)
{
    int i = 0;
    foreach (var item in Napisy())
    {
        i++;
        Console.WriteLine(item);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A tutaj przy pomocy słowa kluczowego "yield" tworzę wirtualną kolekcję, kt&amp;oacute;ra jest nieskończona.&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;public static class YieldCollections
{
    public static IEnumerable&amp;lt;int&amp;gt; RandomNumberCollection()
    {
    
        while(true)
        {
            Random r = new Random(Guid.NewGuid()
                .GetHashCode());
    
            int number = r.Next();
    
            yield return number;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ten kod pochodzi &lt;a title="10 funkcjonalności do odświeżenia w C#" href="https://cezarywalenciuk.pl/blog/programing/10-powodow-aby-odswiezyc-swoja-wiedze-na-temat-csharp" target="_blank" rel="noopener"&gt;z tego wpisu.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wracajmy jednak do naszego gł&amp;oacute;wnego tematu. Teraz gdy .NET 6 oferuje nam IAsyncEnumerable rodzi się pytanie, kiedy z niego skorzystać.&lt;/p&gt;
&lt;p&gt;Może IAsyncEnumerable powinno zastąpić Task&amp;lt;List&amp;lt;T&amp;gt;&amp;gt; albo Task&amp;lt;IEnumerable&amp;lt;T&amp;gt;&amp;gt;?&lt;/p&gt;
&lt;p&gt;A jeśli tak to dlaczego?&lt;/p&gt;
&lt;p&gt;Moim zdaniem korzystanie z Task, gdy wykonujesz jakąś operację asynchroniczną per iteracje dla danej kolekcji jest w porządku.&lt;/p&gt;
&lt;p&gt;Task też super się spełnia, gdy wiemy, że chcemy zwr&amp;oacute;cić jakąś kolekcję z g&amp;oacute;ry kt&amp;oacute;ra powstała w wyniku operacji asynchronicznej.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Oto przykład takie scenariusza. Pobieramy listę tekst&amp;oacute;w z danego REST API, kt&amp;oacute;ry zwr&amp;oacute;ci tę listę tekst&amp;oacute;w w formacie JSON. Oczywiście ta operacja jest asynchroniczna.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task&amp;lt;List&amp;lt;string&amp;gt;&amp;gt; GetTexts()
{
    using var client = new HttpClient();

    var texts = await client.
        GetFromJsonAsync&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;
        ("/api/texts");

    return texts;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto uruchomienie tej metody&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;await GetTexts();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IEnumerable&amp;lt;string&amp;gt; wydaje się bezsensowny. Iteracja asynchroniczna oczywiście nie zajdzie. Sam Task powinien nam zwr&amp;oacute;cić kompletną kolekcję i opakowanie tego w IEnumerable jest tylko iluzją&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task&amp;lt;IEnumerable&amp;lt;string&amp;gt;&amp;gt; GetTexts2()
{
    using var client = new HttpClient();
    var ids = new int[] { 11, 22, 33 };
    foreach (var id in ids)
    {
        var text = await client.
        GetFromJsonAsync&amp;lt;string&amp;gt;
            ($"/api/customers?id={id}");

        yield return text;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na szczęście zachowanie leniwego pobierania danych asynchronicznie można osiągnąć w takim spos&amp;oacute;b.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async IAsyncEnumerable&amp;lt;string&amp;gt; GetTexts3()
{
    using var client = new HttpClient();

    var ids = new int[] { 11, 22, 33, 44,
        55, 66, 77 ,88, 99 };

    foreach (var id in ids)
    {
        var text = await client.
            GetFromJsonAsync&amp;lt;string&amp;gt;
            ($"/api/texts?id={id}");

        yield return text;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak każdą technikę asynchroniczną tak i IAsyncEnumerable można zepsuć. Ten przykład poniżej jest jeszcze okej. Co, jeśli jednak "nazwa gry" nie przekazywana, a jest wynikiem działania jeszcze innej metody asynchronicznej.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async IAsyncEnumerable&amp;lt;string&amp;gt; GetTexts4(string game)
{
    using var client = new HttpClient();

    var ids = new int[] { 11, 22, 33 };

    foreach (var id in ids)
    {
        var text = await client.
            GetFromJsonAsync&amp;lt;string&amp;gt;
            ($"/api/texts?id={id}&amp;amp;game={game}");

        yield return text;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto przykład takiej metody asynchronicznej.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task&amp;lt;string&amp;gt; GetGame()
{
    await Task.Delay(4000);
    return "Mortal Kombat 2";
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak to można zrobić źle? Opakowanie IAsyncEnumerable w Task psuje cały mechanizm strumieniowania.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;//BAD IDEA
async Task&amp;lt;IAsyncEnumerable&amp;lt;string&amp;gt;&amp;gt; GetTextsFromGame()
{
    var game = await GetGame();
    return GetTexts4(game);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lepiej jest to zrobić tak. Pobrać asynchronicznie wszystkie potrzebne parametry, a potem zrobić await foreach.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;//GOOD IDEA
async IAsyncEnumerable&amp;lt;string&amp;gt; GetTexts5()
{
    var game = await GetGame();
    await foreach (var c in GetTexts4(game))
    {
        yield return c;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W ten spos&amp;oacute;b możemy skorzystać z "IAsyncEnumerable"&lt;/p&gt;
&lt;h3&gt;IAsyncEnumerable, a LINQ&lt;/h3&gt;
&lt;p&gt;Używanie LINQ na leniwych kolekcjach strumieniowych jest miejscami dziwne. Zobaczmy, o co chodzi. Oto przykład metody, kt&amp;oacute;ra zwr&amp;oacute;ci mi listę folder&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async IAsyncEnumerable&amp;lt;string&amp;gt; GetFolders()
{
    List&amp;lt;string&amp;gt; folders = new List&amp;lt;string&amp;gt;()
    {
        @"D:\__zdjecia",
        @"D:\_smieciedoanalizy",
        @"D:\----------------",
        @"D:\00",
        @"D:\0Prezentacja",
        @"D:\Camera",
    };

    foreach (var item in folders)
    {
        await Task.Delay(4000);
        yield return item;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto inna metoda, kt&amp;oacute;ra zwr&amp;oacute;ci mi listę informacji o plikach w danym folderze.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async IAsyncEnumerable&amp;lt;FileInfo&amp;gt; GetFilesInfoAsync(string folder)
{
    var directory = new DirectoryInfo(folder);

    foreach (var item in directory.
           GetFiles("*", SearchOption.TopDirectoryOnly))
    {
        await Task.Delay(100);
        yield return item;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto podobna metoda do poprzedniej ta tylko zwraca Taska, a nie IAsyncEnumerable.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task&amp;lt;FileInfo[]&amp;gt; GetFilesInfoTaskAsync(string folder)
{
    await Task.Delay(100);
    var directory = new DirectoryInfo(folder);

    return directory.
           GetFiles("*", SearchOption.TopDirectoryOnly);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stworzyłem też proste metody, kt&amp;oacute;re sprawdzają, czy dany plik jest podejrzany. Jedna metoda jest asynchroniczna, a druga nie&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task&amp;lt;bool&amp;gt; IsSusAsync(FileInfo file)
{
    await Task.Delay(2000);
    Random r = new Random();

    return r.Next(1, 10) &amp;gt; 8;
}

bool IsSus(FileInfo file)
{
    Random r = new Random();

    return r.Next(1, 10) &amp;gt; 8;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Całą kolekcję IAsyncEnumerable można w każdej chwili zamienić na listę korzystając z ToListAsync().&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;List&amp;lt;string&amp;gt; foldersExampleLinqAsync = await GetFolders().ToListAsync();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatywnie można zawsze stworzyć swoją listę używając "await foreach".&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;List&amp;lt;string&amp;gt; folders = new List&amp;lt;string&amp;gt;();
await foreach (var folder in GetFolders())
{
    folders.Add(folder);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To jak to działa z LINQ. Mamy Select z Select, kt&amp;oacute;re wyciągają dane ze dw&amp;oacute;ch strumieni.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection1_1 = GetFolders()
                    .Select(f =&amp;gt; GetFilesInfoAsync(f))
                    .Select(f =&amp;gt; f.Where(file =&amp;gt; IsSus(file)));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Taki kod jest poprawny i LINQ takie, jakie jest da sobie radę. W końcu select z select robi dwie pętle await foreach pod stołem.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;await foreach(var fileinfos in collection1_1)
{
    await foreach(var item in fileinfos)
    {
        Console.WriteLine(item.Name);
        Console.Write($" -&amp;gt; {item.Length}\n");
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Taki kod jest nadal w porządku.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection1_2 = folders
                    .Select(f =&amp;gt; GetFilesInfoAsync(f))
                    .Select
                    (files =&amp;gt; files.Select
                    (file =&amp;gt;
                        new
                        {
                            IsSus = IsSus(file),
                            Name = file.Name,
                            FullName = file.FullName,
                            Lenght = file.Length,
                        }
                    ));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Czy tu będą problemy? W końcu wewnątrz SELECT-a robimy tym razem operację sprawdzania pliku asynchronicznie.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection2_2 = folders
                    .Select(f =&amp;gt; GetFilesInfoAsync(f))
                    .Select
                    (files =&amp;gt; files.Select
                    (async file =&amp;gt;
                        new
                        {
                            IsSus = await IsSusAsync(file),
                            Name = file.Name,
                            FullName = file.FullName,
                            Lenght = file.Length,
                        }
                    ));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Taki kod spokojnie się skompiluje i zadziała.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Problemy zaczynają się, gdy pr&amp;oacute;bujemy tę asynchroniczną metodę wykonać wewnątrz wyrażenia Where.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection2_1 = folders
                    .Select(f =&amp;gt; GetFilesInfoAsync(f))
                    .Select(f =&amp;gt;
                    f.Where(async file =&amp;gt; await IsSusAsync(file)));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Co wtedy nam pozostaje? Zamienić metodę asynchroniczną na synchroniczną blokującą poprzez właściwość Result. W akcje desperacji możemy się podjąć takiej akcji.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection2_1_1 = folders
                    .Select(f =&amp;gt; GetFilesInfoAsync(f))
                    .Select(f =&amp;gt; f.Where(file =&amp;gt; IsSusAsync(file).Result));

var collection2_2_1 = folders
                    .Select(f =&amp;gt; new DirectoryInfo(f).
                    GetFiles("*", SearchOption.TopDirectoryOnly))
                    .Select
                    (files =&amp;gt; files.Select
                    (file =&amp;gt; 
                        new 
                        {
                            IsSus = IsSusAsync(file).Result,
                            Name = file.Name,
                            FullName = file.FullName,
                            Lenght = file.Length,
                        }
                    ));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A jak to możemy zrobić lepiej? Wypadałoby napisać swoją metodę WhereAwait(), kt&amp;oacute;ra by nie miała problem&amp;oacute;w z operacjami "async i await" wewnątrz wyrażenia lambda&lt;/p&gt;
&lt;p&gt;Tylko po co coś takiego pisać, gdy zapewne jest na to paczka NuGet.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/iasyncenumerable-w-c/paczka_638059245064717959.PNG" alt="Paczka Nuget System.LINQ.Async" width="711" height="70" /&gt;&lt;/p&gt;
&lt;p&gt;Oto przykład działania metody WhereAwait(). Dzięki niej cała ta operacja nadal jest asynchronicznym strumieniem.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;IAsyncEnumerable&amp;lt;string&amp;gt; folders = GetFolders();

var collection3_1 = folders
                    .Select( f =&amp;gt; GetFilesInfoAsync(f))
                    .Select(f =&amp;gt; f.WhereAwait
                    (async file =&amp;gt; await IsSusAsync(file)));

await foreach (var fileinfos in collection3_1)
{
    await foreach (var item in fileinfos)
    {
        Console.WriteLine(item.Name);
        Console.Write($" -&amp;gt; {item.Length}\n");
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paczka ta także oferuję metodę SelectAwait. Ona na tym etapie skrystalizuje nas asynchroniczny strumień.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection3_2 = folders
                    .SelectAwait(async f =&amp;gt; await GetFilesInfoAsync2(f))
                    .Select(f =&amp;gt; f.WhereAwait
                    (async file =&amp;gt; await IsSusAsync(file)));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ta sama paczka NuGet ma metodę "ToAsyncEnumerable()" , kt&amp;oacute;ra potrafi zamieniać normalne listy na kolekcję IAsyncEnumerable.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Kiedy taka konwersja ma sens, o tym w następnym przykładzie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var collection3_3 = folders
                    .SelectAwait(async f =&amp;gt; await GetFilesInfoAsync2(f))
                    .Select(f =&amp;gt; f.ToAsyncEnumerable().WhereAwait
                    (async file =&amp;gt; await IsSusAsync(file)));&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Asynchronicznie sekwencyjnie, asynchronicznie wsp&amp;oacute;łbieżnie&lt;/h3&gt;
&lt;p&gt;Do następnego przykładu stworzyłem sobie listę stron internetowych.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var urls = new List&amp;lt;string&amp;gt;()
{
    "https://cezarywalenciuk.pl/",
    "https://docs.microsoft.com/pl-pl/aspnet/core/blazor/",
    "https://angular.io/",
    "https://pl.reactjs.org/",
    "https://vuejs.org/",
    "https://svelte.dev/",
    "https://www.javascript.com/",
    "https://www.youtube.com/",
    "https://azure.microsoft.com/",
    "https://www.amazon.pl/",
    "https://zetcode.com/csharp/httpclient/",
    "https://stackoverflow.com/questions/55686928/using-stopwatch-in-c-sharp",
    "https://www.programmingwithwolfgang.com/replace-rabbitmq-azure-service-bus-queue/",
    "https://medium.com/dotnet-hub/use-azure-key-vault-with-net-or-asp-net-core-applications-read-azure-key-vault-secret-in-dotnet-fca293e9fbb3",
    "https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/attribute-mapping.html",
    "https://www.nuget.org/packages/System.Linq.Async",
    "https://github.com/dotnet/reactive",
    "https://www.udemy.com/",
    "https://jquery.com/",
    "https://www.php.net/",
    "https://www.python.org/",
    "https://go.dev/",
    "https://docs.microsoft.com/pl-pl/dotnet/csharp/"

};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Moim zadaniem jest pobrać zawartość każdej strony internetowej z tej listy po to, aby m&amp;oacute;gł sprawdzić potem, czy te strony internetowe zawierają słowo podane przez użytkownika.&lt;/p&gt;
&lt;p&gt;Oto rozwiązania numer 1 przy użyciu IAsyncEnumerable. Jak widzisz w tym przypadku użycie "ToAsyncEnumerable()" ma sens. Przy pomocy tej metody z Linq.Async zamieniam listę stron na asynchroniczny strumień, kt&amp;oacute;rego każdy elementy będzie wykonywał metodę GetStringAsync z klasy HttpClient.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;async Task SolutionOne()
{
    using var client = new HttpClient();
    Console.WriteLine();
    Console.WriteLine("Wpisz szukane słowo");
    var word = Console.ReadLine();
    if (string.IsNullOrEmpty(word))
        return;

    var timer = new Stopwatch(); timer.Start();

    var results = urls.ToAsyncEnumerable()
            .SelectAwait(async url =&amp;gt;
                new {
                    Url = url,
                    Html = await client.GetStringAsync(url)
                })
            .Where(x =&amp;gt; x.Html.Contains(word));


    await foreach (var result in results)
    {
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine($"Znalezione {result.Url}");
        Console.ResetColor();
    }

    timer.Stop();
    Console.WriteLine(timer.ElapsedMilliseconds);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jaki jest problem z tym rozwiązaniem. Specjalnie napisałem taki przykłady, aby pokazać, że IAsyncEnumerable nie w każdym scenariuszu jest użyteczne.&lt;/p&gt;
&lt;p&gt;Strumień IAsyncEnumerable jest sekwencyjni. Znaczy to, że każda operacja asynchroniczna zapisana w taki spos&amp;oacute;b czeka na poprzednią.&lt;/p&gt;
&lt;p&gt;Być może gdybyśmy szukali pierwszej lepszej strony internetowej gdzie pada dane słowo wtedy takie rozwiązanie miało sens. Przerywalibyśmy wtedy sekwencje.&lt;/p&gt;
&lt;p&gt;My jednak wiemy, że pobierzemy zawsze wszystkie strony internetowe. Jeśli zależy nam na prędkości rozwiązania to oczywiście te strony chcemy pobrać wsp&amp;oacute;łbieżnie, czyli r&amp;oacute;wnocześnie.&lt;/p&gt;
&lt;p&gt;Oto przykład rozwiązania tego samego zadania, ale przy użyciu Task.WhenAll&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;//Przetwarzanie sekwencji r&amp;oacute;wnolegle
async Task SolutionTwo()
{
    using var client = new HttpClient();
    Console.WriteLine();
    Console.WriteLine("Wpisz szukane słowo");
    var word = Console.ReadLine();
    if (string.IsNullOrEmpty(word))
    return;

    var tasks = urls
        .Select(async url =&amp;gt; new
        {
            Url = url,
            Html = await client.GetStringAsync(url)
        });

    var timer = new Stopwatch(); timer.Start();

    var results2 = await Task.WhenAll(tasks);

    foreach (var result in results2.Where(x =&amp;gt; 
        x.Html.Contains(word)))
    {
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine($"Znalezione {result.Url}");
        Console.ResetColor();
    }

    timer.Stop();
    Console.WriteLine(timer.ElapsedMilliseconds);

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatywnie możemy skorzystać z ForEachAsync.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;//R&amp;oacute;wnolegle
async Task SolutionThree()
{
    using var client = new HttpClient();
    Console.WriteLine();
    Console.WriteLine("Wpisz szukane słowo");
    var word = Console.ReadLine();
    if (string.IsNullOrEmpty(word))
        return;

    var parallelOptions = new ParallelOptions() 
    { MaxDegreeOfParallelism = 4 };

    var timer = new Stopwatch(); timer.Start();

    await Parallel.ForEachAsync(urls, parallelOptions,
                async (url, ct) 
                =&amp;gt; await FindMatch(url, word, client));

    timer.Stop();
    Console.WriteLine(timer.ElapsedMilliseconds);
}

async Task FindMatch(string url, string searchTerm, HttpClient client)
{
    var html = await client.GetStringAsync(url);
    if (html.Contains(searchTerm))
    {
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine($"Znalezione {url}");
        Console.ResetColor();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Co jest najszybsze? Na pewno nie "IAsyncEnumerable" po jest ono sekwencyjnie.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Zostają więc dwaj kandydaci Task.WhenAll i Parallel.ForEachAsync.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;while (true)
{
    var options = Console.ReadKey();

    if (options.KeyChar == '1')
        await SolutionOne();
    else if (options.KeyChar == '2')
        await SolutionTwo();
    else if (options.KeyChar == '3')
        await SolutionThree();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W moich testach wychodzi, że to Task.WhenAll jest szybsze. Jednakże warto zaznaczyć, że operacja pobierania strony nie jest operacją deterministyczną i z r&amp;oacute;żnych losowych przyczyn może ona raz zadziałać szybciej lub wolniej.&lt;/p&gt;
&lt;p&gt;To wszystko, co musisz wiedzieć na temat IAsyncEnumerable.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/iasyncenumerable-w-c</guid>
  <pubDate>Sat, 16 Jul 2022 09:46:45 GMT</pubDate>
</item>
<item>
  <title>Blazor i Uwierzytelnienie z JSON Web Tokenem</title>
  <link>https://cezarywalenciuk.pl/blog/programing/blazor-i-uwierzytelnienie-z-json-web-tokenem</link>
  <description>&lt;p&gt;[metro] W poprzednim wpisie stworzyliśmy REST API z ASP.NET CORE w .NET 5/6 wraz z możliwością logowania użytkownik&amp;oacute;w przez JSON WEB Tokeny.&lt;/p&gt;
&lt;p&gt;Jeśli interesuje Cię ta część to proszę sp&amp;oacute;jrz na ten &lt;a title="Uwierzytelnienie z JSON Web Token, Cebula z ASP.NET CORE, Swagger" href="https://cezarywalenciuk.pl/blog/programing/uwierzytelnienie-z-json-web-token-cebula-z-aspnet-core-swagger" target="_blank" rel="noopener"&gt;wpis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nasz projekt wygląda tak:[more]&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="https://cezarywalenciuk.pl/Posts/programing/files/2021/json-web-token-i-samuraje-cebula-z-aspnet-core--swagger-ui-i-blazor/z-11_637589995575461025.PNG" alt="Dodajemy Autoryzację: Samurai.Application.Security w Visual Studio" /&gt;&lt;/p&gt;
&lt;p&gt;Teraz zobaczymy jak obsłużyć REST API z JWT służące nam do uwierzytelnienia w aplikacji Blazor.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-pr_637853723849045706.PNG" alt="Visual Studio Blazor UI Warstwa" width="292" height="397" /&gt;&lt;/p&gt;
&lt;h3&gt;Jakie paczki NuGet ?&lt;/h3&gt;
&lt;p&gt;Od czego tutaj zacząć ? Ot&amp;oacute;ż do nowego projektu trzeba dodać pewne paczki NuGet.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-blazor1_637854270704968734.PNG" alt="blazor webassembly" width="507" height="104" /&gt;&lt;/p&gt;
&lt;p&gt;Na pewno będziemy potrzebowali paczki, kt&amp;oacute;ra będzie nam deserializowała JSON z odpowiedzi HTTP. Klasy, kt&amp;oacute;re p&amp;oacute;źniej wygenerujemy będą korzystać z tej paczki&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-b04_637853737872176756.PNG" alt="Newtonsoft.Json paczka NuGet" width="420" height="66" /&gt;&lt;/p&gt;
&lt;p&gt;Będziemy także potrzebować paczki "Microsoft.Extensions.Http", aby mieć większą kontrolę nad tworzeniem klasy HTTPClient.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-b04_637853737872100226.PNG" alt="Microsoft.Extensions.Http paczka NuGet" width="780" height="79" /&gt;&lt;/p&gt;
&lt;p&gt;Skoro używamy autoryzacji to potrzebujemy paczki "Microsoft.AspNetCore.Authorization", kt&amp;oacute;ra została stworzona z myślą o Blazor.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-b04_637853737872131009.PNG" alt="Microsoft.AspNetCore.Authorization paczka NuGet" width="520" height="69" /&gt;&lt;/p&gt;
&lt;p&gt;Przejdźmy do naszych klas modeli.&lt;/p&gt;
&lt;h3&gt;ViewModels i Mapowanie&lt;/h3&gt;
&lt;p&gt;Jak pamiętasz z poprzedniego przykładu nasze REST API nie jest bogate w klasy. To dobrze, ponieważ nie musimy do nich tworzyć klasy "ViewModel".&lt;/p&gt;
&lt;p&gt;O co chodzi? Ot&amp;oacute;ż, aby moje projekty były bardziej elastyczne i niezależne od siebie to chce, aby każda obiekt, kt&amp;oacute;ry wyjdzie z jako odpowiedź z REST API miał swoją klasę przeznaczoną do wyświetlania tych danych tylko po stronie widoku.&lt;/p&gt;
&lt;p&gt;Jeśli coś zmienię w REST API np. definicję Warrior to szanse, że coś mi eksploduje po stronie UI Blazor będą dużo bardziej zmniejszone.&lt;/p&gt;
&lt;p&gt;Oto wszystkie nasze ViewModele. Jak widzisz po stronie widoku będę chciał wyświetlić Użytkownika, przekazać dane do logowania oraz wyświetlić moich wojownik&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class LoginBlazorVM
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public string Token { get; set; }
}

public class WarriorViewModel
{
    public string Name { get; set; }
    public string ImageUrl { get; set; }
    public int Age { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Projekt jest mały, ale możemy skorzystać z AutoMapper, kt&amp;oacute;ry automatycznie nam z mapuje nam obiekty klas, kt&amp;oacute;re wyjdą z REST API na nasze ViewModele.&lt;/p&gt;
&lt;p&gt;Więcej o AutoMapper pisałem &lt;a title="https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core" href="https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core" target="_blank" rel="noopener"&gt;tutaj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Instalujemy odpowiednie paczki NuGet&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-b095_637853737872309792.PNG" alt="paczka Nuget AutoMapper" width="614" height="151" /&gt;&lt;/p&gt;
&lt;p&gt;Tworzymy definicję mapowania naszego wojownika w obie strony.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public MappingProfile()
{
    CreateMap&amp;lt;WarriorViewModel, Warrior&amp;gt;().ReverseMap();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A na końcu do klasy Program.cs dodajemy rejestracje naszego AutoMappera.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add&amp;lt;App&amp;gt;("#app");
    
    //builder.Services.AddScoped(sp =&amp;gt; new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
    builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz gdy to mamy możemy przejść do najtrudniejszej części. Czyli do tego, jak te dane będziemy pobierać z REST API.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-pro1_637854270705111586.PNG" alt="Visual Studio projekt Blazor" width="224" height="358" /&gt;&lt;/p&gt;
&lt;h3&gt;A może tak wygenerować sobie metody do HTTPClient&lt;/h3&gt;
&lt;p&gt;Gdy pisałem ten projekt na potrzeby prelekcji na Warszawskie Dni Informatyki naprawdę nie chciało mi się ręcznie pisać wywołań HTTPClinet do mojego REST API w aplikacji Blazor.&lt;/p&gt;
&lt;p&gt;Na szczęście możemy wygenerować taki kod, ponieważ nasze REST API używa Swagger do dokumentacji i testu.&lt;/p&gt;
&lt;p&gt;Na pomoc przychodzi NSwag narzędzie, kt&amp;oacute;re pozwoli nam wygenerować API&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-slajd_637853723849140871.png" alt="Co to jest NSwag ? Generator kodu" width="600" height="338" /&gt;&lt;/p&gt;
&lt;p&gt;Instalator do tego programu znajdziesz tutaj :&amp;nbsp;&lt;a href="https://github.com/RicoSuter/NSwag/wiki/NSwagStudio" target="_blank" rel="noopener"&gt;https://github.com/RicoSuter/NSwag/wiki/NSwagStudio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Korzystanie z tego narzędzia jest bardzo proste. Podaje w odpowiednim miejscu definicję JSON swojego Swaggera, a po drugiej stronie mogę zobaczyć wygenerowane wywołanie HTTPClient do tych metod w moim REST API&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-AA_637853723849295339.png" alt="NSwagStudio program" width="750" height="469" /&gt;&lt;/p&gt;
&lt;p&gt;Wygenerowana klasa jednak nie wyręcza mnie we wszystkim. Muszę ją trochę przerobić, aby JSON WEB Tokeny działały. Tego fragmentu kodu nie jestem w stanie wygenerować.&lt;/p&gt;
&lt;p&gt;Do wygenerowanego interfejsu&amp;nbsp; IClient muszę dodać właściwość, kt&amp;oacute;ra wystawi mi do użytku instancje HttpClienta&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public partial interface IClient
{
    System.Net.Http.HttpClient HttpClient { get; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;Potem w wygenerowanej klasie muszę w konstruktorze dodać wstrzyknięcie tego HTTPClienta tak, aby miał nad nim większą kontrolę. To jest moja modyfikacja wygenerowanego kodu.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public partial class Client : IClient
{
    private string _baseUrl = "";
    private System.Net.Http.HttpClient _httpClient;
    private System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt; _settings;
    
    public Client(string baseUrl, System.Net.Http.HttpClient httpClient)
    {
        BaseUrl = baseUrl; 
        _httpClient = httpClient; 
        _settings = new System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt;(CreateSerializerSettings);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dodatkowo z konstruktora wywalam baseUrl, gdyż tak naprawdę utrudnia mi to operację wstrzykiwania zależności.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.10.8.0 (NJsonSchema v10.3.11.0 (Newtonsoft.Json v12.0.0.0))")]
public partial class Client : IClient
{
    private string _baseUrl = "";
    private System.Net.Http.HttpClient _httpClient;
    private System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt; _settings;
    
    public Client(System.Net.Http.HttpClient httpClient)
    {
    
        _httpClient = httpClient;
        _settings = new System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt;(CreateSerializerSettings);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cały wygenerowany kod wygląda tak.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;namespace Samurai.UI.Services
{
    using System = global::System;

    [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.10.8.0 (NJsonSchema v10.3.11.0 (Newtonsoft.Json v12.0.0.0))")]
    public partial interface IClient
    {
        System.Net.Http.HttpClient HttpClient { get; }

        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;AuthenticationResponse&amp;gt; AuthenticateAsync(AuthenticationRequest body);

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;AuthenticationResponse&amp;gt; AuthenticateAsync(AuthenticationRequest body, System.Threading.CancellationToken cancellationToken);

        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;RegistrationResponse&amp;gt; RegisterAsync(RegistrationRequest body);

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;RegistrationResponse&amp;gt; RegisterAsync(RegistrationRequest body, System.Threading.CancellationToken cancellationToken);

        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;System.Collections.Generic.ICollection&amp;lt;Warrior&amp;gt;&amp;gt; GetAllSamuraisAsync();

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        System.Threading.Tasks.Task&amp;lt;System.Collections.Generic.ICollection&amp;lt;Warrior&amp;gt;&amp;gt; GetAllSamuraisAsync(System.Threading.CancellationToken cancellationToken);

    }

    [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.10.8.0 (NJsonSchema v10.3.11.0 (Newtonsoft.Json v12.0.0.0))")]
    public partial class Client : IClient
    {
        private string _baseUrl = "";
        private System.Net.Http.HttpClient _httpClient;
        private System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt; _settings;

        public Client(System.Net.Http.HttpClient httpClient)
        {

            _httpClient = httpClient;
            _settings = new System.Lazy&amp;lt;Newtonsoft.Json.JsonSerializerSettings&amp;gt;(CreateSerializerSettings);
        }
        public System.Net.Http.HttpClient HttpClient
        {
            get { return _httpClient; }
        }

        private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
        {
            var settings = new Newtonsoft.Json.JsonSerializerSettings();
            UpdateJsonSerializerSettings(settings);
            return settings;
        }

        public string BaseUrl
        {
            get { return _baseUrl; }
            set { _baseUrl = value; }
        }

        protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }

        partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);


        partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
        partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
        partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public System.Threading.Tasks.Task&amp;lt;AuthenticationResponse&amp;gt; AuthenticateAsync(AuthenticationRequest body)
        {
            return AuthenticateAsync(body, System.Threading.CancellationToken.None);
        }

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public async System.Threading.Tasks.Task&amp;lt;AuthenticationResponse&amp;gt; AuthenticateAsync(AuthenticationRequest body, System.Threading.CancellationToken cancellationToken)
        {
            if (body == null)
                throw new System.ArgumentNullException("body");

            var urlBuilder_ = new System.Text.StringBuilder();
            urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Login/authenticate");

            var client_ = _httpClient;
            var disposeClient_ = false;
            try
            {
                using (var request_ = new System.Net.Http.HttpRequestMessage())
                {
                    var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
                    content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
                    request_.Content = content_;
                    request_.Method = new System.Net.Http.HttpMethod("POST");
                    request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));

                    PrepareRequest(client_, request_, urlBuilder_);

                    var url_ = urlBuilder_.ToString();
                    request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

                    PrepareRequest(client_, request_, url_);

                    var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
                    var disposeResponse_ = true;
                    try
                    {
                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ =&amp;gt; h_.Key, h_ =&amp;gt; h_.Value);
                        if (response_.Content != null &amp;amp;&amp;amp; response_.Content.Headers != null)
                        {
                            foreach (var item_ in response_.Content.Headers)
                                headers_[item_.Key] = item_.Value;
                        }

                        ProcessResponse(client_, response_);

                        var status_ = (int)response_.StatusCode;
                        if (status_ == 200)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync&amp;lt;AuthenticationResponse&amp;gt;(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            return objectResponse_.Object;
                        }
                        else
                        {
                            var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
                            throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
                        }
                    }
                    finally
                    {
                        if (disposeResponse_)
                            response_.Dispose();
                    }
                }
            }
            finally
            {
                if (disposeClient_)
                    client_.Dispose();
            }
        }

        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public System.Threading.Tasks.Task&amp;lt;RegistrationResponse&amp;gt; RegisterAsync(RegistrationRequest body)
        {
            return RegisterAsync(body, System.Threading.CancellationToken.None);
        }

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public async System.Threading.Tasks.Task&amp;lt;RegistrationResponse&amp;gt; RegisterAsync(RegistrationRequest body, System.Threading.CancellationToken cancellationToken)
        {
            if (body == null)
                throw new System.ArgumentNullException("body");

            var urlBuilder_ = new System.Text.StringBuilder();
            urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Login/register");

            var client_ = _httpClient;
            var disposeClient_ = false;
            try
            {
                using (var request_ = new System.Net.Http.HttpRequestMessage())
                {
                    var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
                    content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
                    request_.Content = content_;
                    request_.Method = new System.Net.Http.HttpMethod("POST");
                    request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));

                    PrepareRequest(client_, request_, urlBuilder_);

                    var url_ = urlBuilder_.ToString();
                    request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

                    PrepareRequest(client_, request_, url_);

                    var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
                    var disposeResponse_ = true;
                    try
                    {
                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ =&amp;gt; h_.Key, h_ =&amp;gt; h_.Value);
                        if (response_.Content != null &amp;amp;&amp;amp; response_.Content.Headers != null)
                        {
                            foreach (var item_ in response_.Content.Headers)
                                headers_[item_.Key] = item_.Value;
                        }

                        ProcessResponse(client_, response_);

                        var status_ = (int)response_.StatusCode;
                        if (status_ == 200)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync&amp;lt;RegistrationResponse&amp;gt;(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            return objectResponse_.Object;
                        }
                        else
                        {
                            var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
                            throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
                        }
                    }
                    finally
                    {
                        if (disposeResponse_)
                            response_.Dispose();
                    }
                }
            }
            finally
            {
                if (disposeClient_)
                    client_.Dispose();
            }
        }

        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public System.Threading.Tasks.Task&amp;lt;System.Collections.Generic.ICollection&amp;lt;Warrior&amp;gt;&amp;gt; GetAllSamuraisAsync()
        {
            return GetAllSamuraisAsync(System.Threading.CancellationToken.None);
        }

        /// &amp;lt;param name="cancellationToken"&amp;gt;A cancellation token that can be used by other objects or threads to receive notice of cancellation.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Success&amp;lt;/returns&amp;gt;
        /// &amp;lt;exception cref="ApiException"&amp;gt;A server side error occurred.&amp;lt;/exception&amp;gt;
        public async System.Threading.Tasks.Task&amp;lt;System.Collections.Generic.ICollection&amp;lt;Warrior&amp;gt;&amp;gt; GetAllSamuraisAsync(System.Threading.CancellationToken cancellationToken)
        {
            var urlBuilder_ = new System.Text.StringBuilder();
            urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Samurai");

            var client_ = _httpClient;
            var disposeClient_ = false;
            try
            {
                using (var request_ = new System.Net.Http.HttpRequestMessage())
                {
                    request_.Method = new System.Net.Http.HttpMethod("GET");
                    request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));

                    PrepareRequest(client_, request_, urlBuilder_);

                    var url_ = urlBuilder_.ToString();
                    request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

                    PrepareRequest(client_, request_, url_);

                    var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
                    var disposeResponse_ = true;
                    try
                    {
                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ =&amp;gt; h_.Key, h_ =&amp;gt; h_.Value);
                        if (response_.Content != null &amp;amp;&amp;amp; response_.Content.Headers != null)
                        {
                            foreach (var item_ in response_.Content.Headers)
                                headers_[item_.Key] = item_.Value;
                        }

                        ProcessResponse(client_, response_);

                        var status_ = (int)response_.StatusCode;
                        if (status_ == 200)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync&amp;lt;System.Collections.Generic.ICollection&amp;lt;Warrior&amp;gt;&amp;gt;(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            return objectResponse_.Object;
                        }
                        else
                        {
                            var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
                            throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
                        }
                    }
                    finally
                    {
                        if (disposeResponse_)
                            response_.Dispose();
                    }
                }
            }
            finally
            {
                if (disposeClient_)
                    client_.Dispose();
            }
        }

        protected struct ObjectResponseResult&amp;lt;T&amp;gt;
        {
            public ObjectResponseResult(T responseObject, string responseText)
            {
                this.Object = responseObject;
                this.Text = responseText;
            }

            public T Object { get; }

            public string Text { get; }
        }

        public bool ReadResponseAsString { get; set; }

        protected virtual async System.Threading.Tasks.Task&amp;lt;ObjectResponseResult&amp;lt;T&amp;gt;&amp;gt; ReadObjectResponseAsync&amp;lt;T&amp;gt;(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary&amp;lt;string, System.Collections.Generic.IEnumerable&amp;lt;string&amp;gt;&amp;gt; headers, System.Threading.CancellationToken cancellationToken)
        {
            if (response == null || response.Content == null)
            {
                return new ObjectResponseResult&amp;lt;T&amp;gt;(default(T), string.Empty);
            }

            if (ReadResponseAsString)
            {
                var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                try
                {
                    var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject&amp;lt;T&amp;gt;(responseText, JsonSerializerSettings);
                    return new ObjectResponseResult&amp;lt;T&amp;gt;(typedBody, responseText);
                }
                catch (Newtonsoft.Json.JsonException exception)
                {
                    var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
                    throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
                }
            }
            else
            {
                try
                {
                    using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    using (var streamReader = new System.IO.StreamReader(responseStream))
                    using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
                    {
                        var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
                        var typedBody = serializer.Deserialize&amp;lt;T&amp;gt;(jsonTextReader);
                        return new ObjectResponseResult&amp;lt;T&amp;gt;(typedBody, string.Empty);
                    }
                }
                catch (Newtonsoft.Json.JsonException exception)
                {
                    var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
                    throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
                }
            }
        }

        private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
        {
            if (value == null)
            {
                return "";
            }

            if (value is System.Enum)
            {
                var name = System.Enum.GetName(value.GetType(), value);
                if (name != null)
                {
                    var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
                    if (field != null)
                    {
                        var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
                            as System.Runtime.Serialization.EnumMemberAttribute;
                        if (attribute != null)
                        {
                            return attribute.Value != null ? attribute.Value : name;
                        }
                    }

                    var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
                    return converted == null ? string.Empty : converted;
                }
            }
            else if (value is bool)
            {
                return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
            }
            else if (value is byte[])
            {
                return System.Convert.ToBase64String((byte[])value);
            }
            else if (value.GetType().IsArray)
            {
                var array = System.Linq.Enumerable.OfType&amp;lt;object&amp;gt;((System.Array)value);
                return string.Join(",", System.Linq.Enumerable.Select(array, o =&amp;gt; ConvertToString(o, cultureInfo)));
            }

            var result = System.Convert.ToString(value, cultureInfo);
            return result == null ? "" : result;
        }
    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.3.11.0 (Newtonsoft.Json v12.0.0.0)")]
    public partial class AuthenticationRequest
    {
        [Newtonsoft.Json.JsonProperty("email", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Email { get; set; }

        [Newtonsoft.Json.JsonProperty("password", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Password { get; set; }


    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.3.11.0 (Newtonsoft.Json v12.0.0.0)")]
    public partial class AuthenticationResponse
    {
        [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Id { get; set; }

        [Newtonsoft.Json.JsonProperty("userName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string UserName { get; set; }

        [Newtonsoft.Json.JsonProperty("email", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Email { get; set; }

        [Newtonsoft.Json.JsonProperty("token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Token { get; set; }


    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.3.11.0 (Newtonsoft.Json v12.0.0.0)")]
    public partial class RegistrationRequest
    {
        [Newtonsoft.Json.JsonProperty("firstName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string FirstName { get; set; }

        [Newtonsoft.Json.JsonProperty("lastName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string LastName { get; set; }

        [Newtonsoft.Json.JsonProperty("email", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Email { get; set; }

        [Newtonsoft.Json.JsonProperty("userName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string UserName { get; set; }

        [Newtonsoft.Json.JsonProperty("password", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Password { get; set; }


    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.3.11.0 (Newtonsoft.Json v12.0.0.0)")]
    public partial class RegistrationResponse
    {
        [Newtonsoft.Json.JsonProperty("userId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string UserId { get; set; }


    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.3.11.0 (Newtonsoft.Json v12.0.0.0)")]
    public partial class Warrior
    {
        [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public string Name { get; set; }

        [Newtonsoft.Json.JsonProperty("age", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public int Age { get; set; }


    }

    [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.10.8.0 (NJsonSchema v10.3.11.0 (Newtonsoft.Json v12.0.0.0))")]
    public partial class ApiException : System.Exception
    {
        public int StatusCode { get; private set; }

        public string Response { get; private set; }

        public System.Collections.Generic.IReadOnlyDictionary&amp;lt;string, System.Collections.Generic.IEnumerable&amp;lt;string&amp;gt;&amp;gt; Headers { get; private set; }

        public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary&amp;lt;string, System.Collections.Generic.IEnumerable&amp;lt;string&amp;gt;&amp;gt; headers, System.Exception innerException)
            : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length &amp;gt;= 512 ? 512 : response.Length)), innerException)
        {
            StatusCode = statusCode;
            Response = response;
            Headers = headers;
        }

        public override string ToString()
        {
            return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
        }
    }

    [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.10.8.0 (NJsonSchema v10.3.11.0 (Newtonsoft.Json v12.0.0.0))")]
    public partial class ApiException&amp;lt;TResult&amp;gt; : ApiException
    {
        public TResult Result { get; private set; }

        public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary&amp;lt;string, System.Collections.Generic.IEnumerable&amp;lt;string&amp;gt;&amp;gt; headers, TResult result, System.Exception innerException)
            : base(message, statusCode, response, headers, innerException)
        {
            Result = result;
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W projekcie do pobranie zostawiłem też plik konfiguracyjny dla programu NSwag.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/wygenerowany kod_637853737872217249.PNG" alt="wygenerowany kod w Visual Studio 2022" width="277" height="230" /&gt;&lt;/p&gt;
&lt;p&gt;Tyle, jeśli chodzi o wywołania naszego REST API. Teraz jak będziemy przesyłać JSON WEB Tokeny do naszego REST API?&lt;/p&gt;
&lt;h3&gt;Jak będziemy dodawać tokeny do zapytania ?&lt;/h3&gt;
&lt;p&gt;Najpierw stworze sobie serwis, kt&amp;oacute;rego zadaniem będzie dodać do instancji wygenerowanego IClient i wewnątrz niego HTTPClient token autoryzujący.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IAddBearerTokenService
{
    Task AddBearerToken(IClient client);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jego implementacja wygląda tak.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class AddBearerTokenService : IAddBearerTokenService
{
    private readonly ILocalStorageService _localStorage;


    public AddBearerTokenService(ILocalStorageService localStorage)
    {
        _localStorage = localStorage;
    }

    public async Task AddBearerToken(IClient client)
    {
        if (await _localStorage.ContainKeyAsync("token"))
            client.HttpClient.DefaultRequestHeaders.Authorization
                = new AuthenticationHeaderValue("Bearer", await _localStorage.GetItemAsync&amp;lt;string&amp;gt;("token"));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Skąd wzięło się "ILocalStorageService". Ot&amp;oacute;ż ten interfejs pochodzi z paczki NuGet : "Blazored.LocalStorage".&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-nuget_637853737872246468.PNG" alt="Paczka NuGet Blazored.LocalStorage" width="436" height="73" /&gt;&lt;/p&gt;
&lt;p&gt;Blazored.LocalStorage pozwoli nam przechowywać r&amp;oacute;żne dane w trakcie działania aplikacji Blazor w tym właśnie nasz token autoryzujący. Ten token zostanie zapisany w przeglądarce i gdy użytkownik uruchomi naszą aplikację Blazor ponownie to nadal będzie on zalogowany.&lt;/p&gt;
&lt;p&gt;Aby użytkownik m&amp;oacute;gł się wylogować to musi ten token skasować ze swojego lokalnego zapisu w przeglądarce.&lt;/p&gt;
&lt;p&gt;[cloud-green Co to jest LocalStorage ?]Zapisane w localStorage obiekty służą do długotrwałego przechowywania danych i pozostają na dysku po zamknięciu przeglądarki.&lt;/p&gt;
&lt;p&gt;W przeciwieństwie do cookies, obiekty z localStorage nie są automatycznie odczytywane przez serwer przy połączeniu&lt;/p&gt;
&lt;p&gt;Firefox, Chrome i Opera rezerwują 10 MB na domenę, a Internet Explorer 100 MB na wszystkie przechowywane dane, przy czym wartość te można zmienić na maksymalnie 20 GB. Dane przechowywane są jako tablice asocjacyjne, kt&amp;oacute;rych kluczem i wartością jest łańcuch znak&amp;oacute;w.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;LocalStorage pojawił się wraz ze standardem HTML 5.&lt;/p&gt;
&lt;p&gt;[cg]&lt;/p&gt;
&lt;p&gt;Pamiętaj, że każda aplikacja internetowa pisana JavaScript także ma dostęp do "localStorage"&lt;/p&gt;
&lt;p&gt;Teraz stworzę serwis, kt&amp;oacute;ry będzie wyciągał moich samuraj&amp;oacute;w wojownik&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface ISamuraiService
{
    Task&amp;lt;List&amp;lt;WarriorViewModel&amp;gt;&amp;gt; GetAllWarriors();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jego implementacja wygląda tak&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SamuraiService : ISamuraiService
{
    private readonly IMapper _mapper;
    private IClient _client;
    private IAddBearerTokenService _addBearerTokenService;

    public SamuraiService(IClient client, IMapper mapper,
        IAddBearerTokenService addBearerTokenService)
    {
        _mapper = mapper;
        _client = client;
        _addBearerTokenService = addBearerTokenService;
    }

    public async Task&amp;lt;List&amp;lt;WarriorViewModel&amp;gt;&amp;gt; GetAllWarriors()
    {
        await _addBearerTokenService.AddBearerToken(_client);

        try
        {
            var allPosts = await _client.GetAllSamuraisAsync();
            var mappedPosts = _mapper.Map&amp;lt;ICollection&amp;lt;WarriorViewModel&amp;gt;&amp;gt;(allPosts);
            return mappedPosts.ToList();
        }
        catch (ApiException ex)
        {
            return new List&amp;lt;WarriorViewModel&amp;gt;();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak widzisz przed wywołaniem odpowiedniej metody z wygenerowanego kodu IClient spr&amp;oacute;buje dodać wcześniej token autoryzujący. Jak pamiętasz m&amp;oacute;j serwis "IAddBearerTokenService" doda ten token do instancji HTTPClient tak, aby m&amp;oacute;gł on zostać wysłany wraz z zapytaniem HTTP.&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Jak będziemy się logować i pobierać ten token ?&lt;/h3&gt;
&lt;p&gt;A jak ten token pobierać ? To za chwilę, bo mamy ważniejsze pytanie.&lt;/p&gt;
&lt;p&gt;Jak w Blazor przedstawić użytkownika, aby można było skorzystać z pewnych specjalnych opcji i r&amp;oacute;l w wewnątrz tej aplikacji. Nie chcemy takie weryfikacji od zera.&amp;nbsp; Zobaczmy co Blazor nam oferuje.&lt;/p&gt;
&lt;p&gt;Do tej zabawy musimy stworzyć swoją implemtnację klasy abstrakcyjnej "AuthenticationStateProvider". Wygląda ona tak:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ILocalStorageService _localStorage;

    public CustomAuthenticationStateProvider(ILocalStorageService localStorage)
    {
        _localStorage = localStorage;
    }

    public override async Task&amp;lt;AuthenticationState&amp;gt; GetAuthenticationStateAsync()
    {
        var savedToken = await _localStorage.GetItemAsync&amp;lt;string&amp;gt;("token");

        if (string.IsNullOrWhiteSpace(savedToken))
        {
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }

        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseTokenClaims(savedToken), "jwt")));
    }

    public void SetUserAuthenticated(string email)
    {
        var authUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, email) }, "apiauth"));
        var authState = Task.FromResult(new AuthenticationState(authUser));
        NotifyAuthenticationStateChanged(authState);
    }

    public void SetUserLoggedOut()
    {
        var anonUser = new ClaimsPrincipal(new ClaimsIdentity());
        var authState = Task.FromResult(new AuthenticationState(anonUser));
        NotifyAuthenticationStateChanged(authState);
    }

    private IEnumerable&amp;lt;Claim&amp;gt; ParseTokenClaims(string jwt)
    {
        var claims = new List&amp;lt;Claim&amp;gt;();
        var payload = jwt.Split('.')[1];
        var jsonBytes = ParseBase64WithoutPadding(payload);
        var keyValuePairs = JsonSerializer.Deserialize&amp;lt;Dictionary&amp;lt;string, object&amp;gt;&amp;gt;(jsonBytes);

        keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);

        if (roles != null)
        {
            if (roles.ToString().Trim().StartsWith("["))
            {
                var parsedRoles = JsonSerializer.Deserialize&amp;lt;string[]&amp;gt;(roles.ToString());

                foreach (var parsedRole in parsedRoles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, parsedRole));
                }
            }
            else
            {
                claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
            }

            keyValuePairs.Remove(ClaimTypes.Role);
        }

        claims.AddRange(keyValuePairs.Select(kvp =&amp;gt; new Claim(kvp.Key, kvp.Value.ToString())));

        return claims;
    }

    private byte[] ParseBase64WithoutPadding(string base64)
    {
        switch (base64.Length % 4)
        {
            case 2: base64 += "=="; break;
            case 3: base64 += "="; break;
        }
        return Convert.FromBase64String(base64);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W metodzie przeciążonej "GetAuthenticationStateAsync" zwracam stan zalogowanego użytkownika wraz z jego rolami.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public override async Task&amp;lt;AuthenticationState&amp;gt; GetAuthenticationStateAsync()
{
    var savedToken = await _localStorage.GetItemAsync&amp;lt;string&amp;gt;("token");

    if (string.IsNullOrWhiteSpace(savedToken))
    {
        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
    }

    return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseTokenClaims(savedToken), "jwt")));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pierwsza metoda pozwoli nam ustawić użytkownika w aplikacji Blazor, gdy go zweryfikujemy.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public void SetUserAuthenticated(string email)
{
    var authUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, email) }, "apiauth"));
    var authState = Task.FromResult(new AuthenticationState(authUser));
    NotifyAuthenticationStateChanged(authState);
}

public void SetUserLoggedOut()
{
    var anonUser = new ClaimsPrincipal(new ClaimsIdentity());
    var authState = Task.FromResult(new AuthenticationState(anonUser));
    NotifyAuthenticationStateChanged(authState);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Z drugiej metody skorzystamy, gdy będziemy chcieli użytkownika wylogować. Informując przy tym aplikację Blazor, że status się zmienił poprzez wywołanie metody&amp;nbsp;NotifyAuthenticationStateChanged&lt;/p&gt;
&lt;p&gt;A jak wygląda parsowanie JSON WEB Tokenu. Token jest zapisany specyficznie w Base64. Potem gdy uzyskam z tego tablice bajt&amp;oacute;w to mogę to z deserializować na słownik klucz / wartość.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;private IEnumerable&amp;lt;Claim&amp;gt; ParseTokenClaims(string jwt)
{
    var claims = new List&amp;lt;Claim&amp;gt;();
    var payload = jwt.Split('.')[1];
    var jsonBytes = ParseBase64WithoutPadding(payload);
    var keyValuePairs = JsonSerializer.Deserialize&amp;lt;Dictionary&amp;lt;string, object&amp;gt;&amp;gt;(jsonBytes);

    keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);

    if (roles != null)
    {
        if (roles.ToString().Trim().StartsWith("["))
        {
            var parsedRoles = JsonSerializer.Deserialize&amp;lt;string[]&amp;gt;(roles.ToString());

            foreach (var parsedRole in parsedRoles)
            {
                claims.Add(new Claim(ClaimTypes.Role, parsedRole));
            }
        }
        else
        {
            claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
        }

        keyValuePairs.Remove(ClaimTypes.Role);
    }

    claims.AddRange(keyValuePairs.Select(kvp =&amp;gt; new Claim(kvp.Key, kvp.Value.ToString())));

    return claims;
}

private byte[] ParseBase64WithoutPadding(string base64)
{
    switch (base64.Length % 4)
    {
        case 2: base64 += "=="; break;
        case 3: base64 += "="; break;
    }
    return Convert.FromBase64String(base64);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak też wyciągam rolę zapisane w tym JSON WEB Tokenie.&lt;/p&gt;
&lt;p&gt;Teraz oddalam się od tej wymaganej klasy przez Blazor i tworzę sw&amp;oacute;j serwis autoryzujący z takimi metodami.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IBlazorAuthenticationService
{
    Task&amp;lt;bool&amp;gt; Authenticate(string email, string password);
    Task&amp;lt;bool&amp;gt; Register(string firstName, string lastName, string userName, string email, string password);
    Task Logout();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto jego implementacja.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class BlazorAuthenticationService : IBlazorAuthenticationService
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;
    protected readonly ILocalStorageService _localStorage;

    protected IClient _client;

    public BlazorAuthenticationService(IClient client, ILocalStorageService localStorage,
        AuthenticationStateProvider authenticationStateProvider)
    {
        _authenticationStateProvider = authenticationStateProvider;
        _client = client;
        _localStorage = localStorage;
    }

    public async Task&amp;lt;bool&amp;gt; Authenticate(string email, string password)
    {
        try
        {
            AuthenticationRequest authenticationRequest = new AuthenticationRequest() { Email = email, Password = password };
            var authenticationResponse = await _client.AuthenticateAsync(authenticationRequest);

            if (authenticationResponse.Token != string.Empty)
            {
                await _localStorage.SetItemAsync("token", authenticationResponse.Token);
                ((CustomAuthenticationStateProvider)_authenticationStateProvider).SetUserAuthenticated(email);
                _client.HttpClient.DefaultRequestHeaders.Authorization
                    = new AuthenticationHeaderValue("bearer", authenticationResponse.Token);
                return true;
            }
            return false;
        }
        catch
        {
            return false;
        }
    }

    public async Task&amp;lt;bool&amp;gt; Register(string firstName, string lastName, string userName, string email, string password)
    {
        RegistrationRequest registrationRequest = new RegistrationRequest() { FirstName = firstName, LastName = lastName, Email = email, UserName = userName, Password = password };
        var response = await _client.RegisterAsync(registrationRequest);

        if (!string.IsNullOrEmpty(response.UserId))
        {
            return true;
        }
        return false;
    }

    public async Task Logout()
    {
        await _localStorage.RemoveItemAsync("token");
        ((CustomAuthenticationStateProvider)_authenticationStateProvider).SetUserLoggedOut();
        _client.HttpClient.DefaultRequestHeaders.Authorization = null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak wyciągamy Token z REST API ?&lt;/p&gt;
&lt;p&gt;Korzystam z pewnej wygenerowanej metody "AuthenticateAsync" przez NSwag i potem zapisuje ten token w LocalStorage.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public async Task&amp;lt;bool&amp;gt; Authenticate(string email, string password)
{
    try
    {
        AuthenticationRequest authenticationRequest = new AuthenticationRequest() { Email = email, Password = password };
        var authenticationResponse = await _client.AuthenticateAsync(authenticationRequest);

        if (authenticationResponse.Token != string.Empty)
        {
            await _localStorage.SetItemAsync("token", authenticationResponse.Token);
            ((CustomAuthenticationStateProvider)_authenticationStateProvider).SetUserAuthenticated(email);
            _client.HttpClient.DefaultRequestHeaders.Authorization
                = new AuthenticationHeaderValue("bearer", authenticationResponse.Token);
            return true;
        }
        return false;
    }
    catch
    {
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wylogowanie wygląda tak. Usuwam token z LocalStorage, a potem m&amp;oacute;j&amp;nbsp;CustomAuthenticationStateProvider informuje Blazor o zmianie statusu użytkownika.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public async Task Logout()
{
    await _localStorage.RemoveItemAsync("token");
    ((CustomAuthenticationStateProvider)_authenticationStateProvider).SetUserLoggedOut();
    _client.HttpClient.DefaultRequestHeaders.Authorization = null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rejestracja to tylko wywołanie odpowiedniej wygenerowanej metody przez NSwag&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public async Task&amp;lt;bool&amp;gt; Register(string firstName, string lastName, string userName, string email, string password)
{
    RegistrationRequest registrationRequest = new RegistrationRequest() { FirstName = firstName, LastName = lastName, Email = email, UserName = userName, Password = password };
    var response = await _client.RegisterAsync(registrationRequest);

    if (!string.IsNullOrEmpty(response.UserId))
    {
        return true;
    }
    return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak obecnie prezentuje się nasz projekt Blazor.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-xcxcx_637854300175049594.PNG" alt="Nasz projekt Blazor w Visual Studio" width="324" height="246" /&gt;&lt;/p&gt;
&lt;p&gt;Korzystając z kontenera wstrzykiwania zależności zarejestrujmy te wszystkie mechanizmy w naszej aplikacji Blazor.&lt;/p&gt;
&lt;p&gt;Jak widzisz ustawiamy tutaj także adres naszego REST API. Tę aplikację do test&amp;oacute;w uruchomię po localhost i pod portem 5001.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.RootComponents.Add&amp;lt;App&amp;gt;("#app");

        //builder.Services.AddScoped(sp =&amp;gt; new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
        builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
        builder.Services.AddBlazoredLocalStorage();

        builder.Services.AddSingleton(new HttpClient
        {
            BaseAddress = new Uri("https://localhost:5001")
        });

        builder.Services.AddHttpClient&amp;lt;IClient, Client&amp;gt;
        (client =&amp;gt; client.BaseAddress = new Uri("https://localhost:5001"));

        builder.Services.AddScoped&amp;lt;ISamuraiService, SamuraiService&amp;gt;();

        //Auth
        builder.Services.AddScoped&amp;lt;IBlazorAuthenticationService,
        BlazorAuthenticationService&amp;gt;();
        builder.Services.AddScoped&amp;lt;AuthenticationStateProvider,
        CustomAuthenticationStateProvider&amp;gt;();
        builder.Services.AddScoped&amp;lt;IAddBearerTokenService,
        AddBearerTokenService&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz możemy przejść do naszych strony pisanych w Razor, czyli składni, kt&amp;oacute;ra pozwala nam mieszać kod HTML z C#&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Razor, czyli strony&lt;/h3&gt;
&lt;p&gt;Oto mojego wszystkie strony.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-pro_637853749518470460.PNG" alt="Strony Razor w naszej aplikacji Blazor" width="248" height="178" /&gt;&lt;/p&gt;
&lt;p&gt;Razor strony logującej wygląda tak. Jak widzisz pola InputText są powiązane, z jaką właściwością "@LoginViewModel.Password".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Samo wywołanie formularza wywoła jakąś metodę "HandleValidSubmit". Gdzie ona jest ? Jest ona tak zwanym CodeBehind.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs razor "&gt;@page "/login"

&amp;lt;div class="mt-5 row"&amp;gt;
    &amp;lt;div class="card col-12 col-lg-6 mr-auto ml-auto p-3"&amp;gt;
        &amp;lt;h3 class="card-title"&amp;gt;Zaloguj się Samuraju&amp;lt;/h3&amp;gt;
        &amp;lt;div class="card-body"&amp;gt;
            &amp;lt;EditForm Model="@LoginViewModel" OnValidSubmit="HandleValidSubmit"&amp;gt;
                &amp;lt;div class="input-group row mt-2"&amp;gt;
                    &amp;lt;label class="col-12 col-md-4 p-0" for="userName"&amp;gt;Email:&amp;lt;/label&amp;gt;
                    &amp;lt;InputText id="userName" class="col-12 col-md-8 login-field" @bind-Value="LoginViewModel.Email"&amp;gt;&amp;lt;/InputText&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="input-group row mt-2"&amp;gt;
                    &amp;lt;label class="col-12 col-md-4 p-0" for="password"&amp;gt;Password:&amp;lt;/label&amp;gt;
                    &amp;lt;InputText type="password" id="password" class="col-12 col-md-8 login-field" @bind-Value="@LoginViewModel.Password"&amp;gt;&amp;lt;/InputText&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="input-group row mt-2"&amp;gt;
                    &amp;lt;div class=""&amp;gt;
                        &amp;lt;button class="m-4 p-2"&amp;gt;Zaloguj&amp;lt;/button&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/EditForm&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;span class="label label-error"&amp;gt;@Message&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kod poboczny, kt&amp;oacute;ry obsłuży logikę tej strony wygląda następująco. Jak widzisz w kodzie pobocznym nie możemy zrobić wstrzykiwania zależności poprzez konstruktor.&lt;/p&gt;
&lt;p&gt;Na pomoc przychodzi nam atrybut "[Inject]", kt&amp;oacute;ry to za nas zrobi.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public partial class Login
{
    public LoginBlazorVM LoginViewModel { get; set; }

    [Inject]
    public NavigationManager NavigationManager { get; set; }
    public string Message { get; set; }

    [Inject]
    private IBlazorAuthenticationService AuthenticationService { get; set; }

    public Login()
    {

    }

    protected override void OnInitialized()
    {
        LoginViewModel = new LoginBlazorVM();
    }

    protected async void HandleValidSubmit()
    {
        if (await AuthenticationService.Authenticate
            (LoginViewModel.Email, LoginViewModel.Password))
        {
            NavigationManager.NavigateTo("adminoptions");
        }
        Message = "Username/password Coś jest źle";
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasza metoda "HandleValidSubmit" wykona zapytanie HTTP do naszego REST API i poprosi o token autoryzujący. Jeśli wszystko poszło zgodnie z planem to zostaniemy przekierowani na stronę "adminoptions".&lt;/p&gt;
&lt;h3&gt;Kontrola r&amp;oacute;l użytkownik&amp;oacute;w&lt;/h3&gt;
&lt;p&gt;W Blazor istnieje także możliwość kontroli r&amp;oacute;l użytkownik&amp;oacute;w. Pr&amp;oacute;bowałem napisać taki kod i przynajmniej jestem w stanie Ci zagwarantować, że działa sprawdzenie, czy użytkownik w og&amp;oacute;le jest zalogowany.&lt;/p&gt;
&lt;p&gt;Mam pewne problemy ze sprawdzeniem konkretnych r&amp;oacute;l i mam nadzieje, że kiedyś znajdę przyczynę tego błędu w kodzie&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Program
{
    public static async Task Main(string[] args)
    {

        ...
        builder.Services.AddAuthorizationCore(config =&amp;gt;
        {
            config.AddPolicy(Policies.IsAdmin, Policies.IsUserLogged());
            config.AddPolicy(Policies.IsUserLog, Policies.IsUserLogged());
            config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
            config.AddPolicy(Policies.IsClaim, Policies.IsClaimed());
        });

        await builder.Build().RunAsync();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Warto stworzyć sobie taką klasę statyczną i weryfikować użytkownik&amp;oacute;w w widoku Razor.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public static class Policies
{
    public const string IsAdmin = "IsAdmin";
    public const string IsUserLog = "IsUserLog";
    public const string IsUser = "IsUser";
    public const string IsClaim = "IsClaim";

    public static AuthorizationPolicy IsAdminPolicy()
    {
        return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()

                                               .RequireRole("adminEdu")
                                               .Build();
    }

    public static AuthorizationPolicy IsUserLogged()
    {
        return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()

                                               .Build();
    }

    public static AuthorizationPolicy IsClaimed()
    {
        return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
            .RequireClaim("MyCos", "MyValue")
            .Build();
    }

    public static AuthorizationPolicy IsUserPolicy()
    {
        return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
                                               .RequireRole("User")
                                               .Build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Wracamy do stron Razor&lt;/h3&gt;
&lt;p&gt;Teraz skorzystam z tej klasy statycznej "Policies". Oto kod Razor "NavMenu.razor"&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs razor "&gt;&amp;lt;div class="top-row pl-4 navbar navbar-dark"&amp;gt;
    &amp;lt;a class="navbar-brand" href=""&amp;gt;Samurai.UI&amp;lt;/a&amp;gt;
    &amp;lt;button class="navbar-toggler" @onclick="ToggleNavMenu"&amp;gt;
        &amp;lt;span class="navbar-toggler-icon"&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class="@NavMenuCssClass" @onclick="ToggleNavMenu"&amp;gt;
    &amp;lt;ul class="nav flex-column"&amp;gt;
        &amp;lt;li class="nav-item px-3"&amp;gt;
            &amp;lt;NavLink class="nav-link" href="" Match="NavLinkMatch.All"&amp;gt;
                &amp;lt;span class="oi oi-home" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Home
            &amp;lt;/NavLink&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item px-3"&amp;gt;
            &amp;lt;NavLink class="nav-link" href="login"&amp;gt;
                &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Login
            &amp;lt;/NavLink&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item px-3"&amp;gt;
            &amp;lt;NavLink class="nav-link" href="samurailist"&amp;gt;
                &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Listy Samuraj&amp;oacute;w
            &amp;lt;/NavLink&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item px-3" Policy="@Policies.IsAdmin"&amp;gt;
            &amp;lt;NavLink class="nav-link" href="adminoptions"&amp;gt;
                &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Admin
            &amp;lt;/NavLink&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass =&amp;gt; collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak widzisz pr&amp;oacute;buje w ten spos&amp;oacute;b ukryć jeden z linku w tej pasku nawigującym.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs razor "&gt;&amp;lt;li class="nav-item px-3" Policy="@Policies.IsAdmin"&amp;gt;
    &amp;lt;NavLink class="nav-link" href="adminoptions"&amp;gt;
        &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Admin
    &amp;lt;/NavLink&amp;gt;
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W widoku można skorzystać z budowanych tag&amp;oacute;w w Razor, kt&amp;oacute;re ukryją odpowiednio zawartość strony przed nie zalogowany użytkownikiem.&lt;/p&gt;
&lt;p&gt;Oto kod strony "AdminOptions.razor". Tutaj wyświetlę sobie zawartość całego tokenu JSON.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs razor "&gt;@page "/adminoptions"

&amp;lt;AuthorizeView Policy="@Policies.IsAdmin"&amp;gt;
    &amp;lt;Authorized&amp;gt;

        &amp;lt;p class="back-to-catalog mt-3"&amp;gt;
            &amp;lt;a class="back-link" @onclick="@NavigateToList"&amp;gt;---- Zobacz listę samuraj&amp;oacute;w ----&amp;lt;/a&amp;gt;
        &amp;lt;/p&amp;gt;
    &amp;lt;/Authorized&amp;gt;
    &amp;lt;NotAuthorized&amp;gt;
        &amp;lt;p&amp;gt;Proszę się zalogować&amp;lt;/p&amp;gt;
    &amp;lt;/NotAuthorized&amp;gt;
&amp;lt;/AuthorizeView&amp;gt;
&amp;lt;br /&amp;gt;

&amp;lt;AuthorizeView Policy="@Policies.IsClaim"&amp;gt;
    &amp;lt;Authorized&amp;gt;
        &amp;lt;p&amp;gt;@Policies.IsClaim : true&amp;lt;/p&amp;gt;
    &amp;lt;/Authorized&amp;gt;
    &amp;lt;NotAuthorized&amp;gt;
        &amp;lt;p&amp;gt;@Policies.IsClaim : false&amp;lt;/p&amp;gt;
    &amp;lt;/NotAuthorized&amp;gt;
&amp;lt;/AuthorizeView&amp;gt;

Zawartość tokenu JSON
&amp;lt;p&amp;gt;
    @Data
&amp;lt;/p&amp;gt;

&amp;lt;p class="back-to-catalog mt-3"&amp;gt;
    &amp;lt;a class="back-link" @onclick="@DeleteToken"&amp;gt;---- Skasuj token ----&amp;lt;/a&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kod poboczny strony wygląda tak. W trakcie ładowania strony wyświetlę sobie zawartość tokenu. Przy okazji oto prosty przykład jak w Blazor tworzyć dynamicznie kod HTML poprzez użycie klasy "MarkupString"&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public partial class AdminOptions
{
    [Inject]
    public AuthenticationStateProvider _authenticationStateProvider { get; set; }

    [Inject]
    protected ILocalStorageService _localStorage { get; set; }

    public AdminOptions()
    {

    }

    [Parameter]
    public MarkupString Data { get; set; } = new MarkupString("");

    protected async override Task OnInitializedAsync()
    {
        var use = (await _authenticationStateProvider.GetAuthenticationStateAsync());

        StringBuilder sb = new StringBuilder();
        sb.AppendLine(use.User.Identity.Name);
        sb.Append("&amp;lt;br /&amp;gt;");

        sb.AppendLine(" &amp;lt;ul&amp;gt; ");
        foreach (var item in use.User.Claims)
        {
            sb.Append(" &amp;lt;li&amp;gt; ");
            sb.Append(" &amp;lt;ol&amp;gt; ");
            sb.Append(" &amp;lt;li&amp;gt; ");
            sb.AppendLine(item.Type);
            sb.Append(" &amp;lt;/li&amp;gt; ");
            sb.Append(" &amp;lt;li&amp;gt; ");
            sb.AppendLine(item.Value);
            sb.Append(" &amp;lt;/li&amp;gt; ");
            sb.Append(" &amp;lt;/ol&amp;gt; ");
            sb.Append(" &amp;lt;/li&amp;gt; ");
        }
        sb.AppendLine(" &amp;lt;/ul&amp;gt; ");
        sb.Append("&amp;lt;br /&amp;gt; Jest Adminiem");
        sb.AppendLine(use.User.IsInRole("Admin").ToString());
        sb.Append("&amp;lt;br /&amp;gt; Ma MyValue");
        sb.AppendLine(use.User.Claims.Any(p =&amp;gt; p.Value == "MyValue").ToString());
        sb.Append("&amp;lt;br /&amp;gt; Ma Cos");
        sb.AppendLine(use.User.Claims.Any(p =&amp;gt; p.Type == "MyCos").ToString());


        Data = new MarkupString(sb.ToString());
    }


    [Inject]
    public NavigationManager NavigationManager { get; set; }

    protected void DeleteToken()
    {
        _localStorage.RemoveItemAsync("token");
        NavigationManager.NavigateTo("/");
    }

    protected void NavigateToList()
    {
        NavigationManager.NavigateTo("/samurailist");
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak wygląda kod strony "SamuraiList.razor". Jej celem jest wyświetlenie wszystkich samuraj&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs razor "&gt;&amp;lt;AuthorizeView Policy="@Policies.IsAdmin"&amp;gt;
    &amp;lt;Authorized&amp;gt;

        &amp;lt;h3&amp;gt;Samuraje&amp;lt;/h3&amp;gt;

        @if (Warriors == null)
        {
            &amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Loading...&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
        }
        else
        {
            &amp;lt;table&amp;gt;
                &amp;lt;thead&amp;gt;
                    &amp;lt;tr&amp;gt;
                        &amp;lt;th&amp;gt;Nazwa&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;Wiek&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
                    &amp;lt;/tr&amp;gt;
                &amp;lt;/thead&amp;gt;
                &amp;lt;tbody&amp;gt;
                    @foreach (var p in Warriors)
                    {
                        &amp;lt;tr&amp;gt;

                            &amp;lt;td class="post-title"&amp;gt;@p.Name &amp;lt;/td&amp;gt;
                            &amp;lt;td class="post-title"&amp;gt;@p.Age &amp;lt;/td&amp;gt;
                            &amp;lt;td class="post-title"&amp;gt;@p.ImageUrl&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    }
                &amp;lt;/tbody&amp;gt;
            &amp;lt;/table&amp;gt;
        }
    &amp;lt;/Authorized&amp;gt;
    &amp;lt;NotAuthorized&amp;gt;
        &amp;lt;p&amp;gt;Proszę się zalogować. Samuraju&amp;lt;/p&amp;gt;
    &amp;lt;/NotAuthorized&amp;gt;
&amp;lt;/AuthorizeView&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto kod poboczny tej strony. Pobieram samuraj&amp;oacute;w w trakcie ładowania strony i potem przekazuje to, co pobrałem do parametru.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public partial class SamuraiList
{
    [Inject]
    public ISamuraiService SamuraiService { get; set; }

    [Inject]
    public NavigationManager NavigationManager { get; set; }

    public ICollection&amp;lt;WarriorViewModel&amp;gt; Warriors { get; set; }

    protected async override Task OnInitializedAsync()
    {
        Warriors = await SamuraiService.GetAllWarriors();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wszystko, zanim uruchomimy naszą aplikację Blazor warto zmienić jej port. Zakładamy, że nasze REST API będzie działać pod portem 5001. To niech nasza aplikacja Blazor działa pod portem 5005.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs json "&gt;{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:50456",
      "sslPort": 44375
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
    },
    "Samurai.UI": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "dotnetRunMessages": "true",
      "applicationUrl": "https://localhost:5005;http://localhost:5006",
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto chwila prawdy.&lt;/p&gt;
&lt;h3&gt;Zobaczmy jak to działa ?&lt;/h3&gt;
&lt;p&gt;Strona startowa wygląda tak:&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518575121.PNG" alt="Strona startowa Blazor" width="519" height="281" /&gt;&lt;/p&gt;
&lt;p&gt;Jak widać nie możemy zobaczyć listy samuraj&amp;oacute;w przed zalogowaniem więc blokady nasze działają.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518610120.PNG" alt="Strona lista samuraj&amp;oacute;w jest zablokowana ponieważ nie jesteśmy zalogowani" width="421" height="248" /&gt;&lt;/p&gt;
&lt;p&gt;W panelu administratora jak widzisz nie mamy żadnych informacji.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518652088.PNG" alt="panelu administratora jest pusty" width="372" height="326" /&gt;&lt;/p&gt;
&lt;p&gt;Uruchamiamy nasze REST API, kt&amp;oacute;re stworzyliśmy w poprzednim wpisie.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-aadd_637853751327787857.PNG" alt="REST API metody" width="487" height="268" /&gt;&lt;/p&gt;
&lt;p&gt;Logujemy się. Mamy użytkownika eve@gmail.com z hasłem 12345&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518688225.PNG" alt="Logujemy się w naszej aplikacji Blazor" width="539" height="370" /&gt;&lt;/p&gt;
&lt;p&gt;Możemy wyświetlić sobie zawartość naszego JSON WEB Tokenu.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518780405.PNG" alt="Zawartość naszego JSON WEB Tokena" width="465" height="826" /&gt;&lt;/p&gt;
&lt;p&gt;Teraz gdy jesteśmy zalogowani to możemy zobaczyć listę samuraj&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/blazor-i-uwierzytelnienie-z-json-web-tokenem/00-sam-08_637853749518814561.PNG" alt="Lista Samuraj&amp;oacute;w w Blazor" width="547" height="263" /&gt;&lt;/p&gt;
&lt;p&gt;To wszystko. Kod można pobrać tutaj&amp;nbsp;&lt;a href="https://github.com/PanNiebieski/example-JsonWebToken-with-Blazor-Samurai"&gt;PanNiebieski/example-JsonWebToken-with-Blazor-Samurai (github.com)&lt;/a&gt;&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <category>blazor</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/blazor-i-uwierzytelnienie-z-json-web-tokenem</guid>
  <pubDate>Tue, 12 Apr 2022 14:36:57 GMT</pubDate>
</item>
<item>
  <title>Architekt Programista? 6 pytań do przyszłego Ciebie</title>
  <link>https://cezarywalenciuk.pl/blog/programing/architekt-programista-6-pytan-do-przyszlego-ciebie</link>
  <description>&lt;p&gt;[metro]Każdy chce szybko z programisty stać się architektem, aby oczywiście mieć więcej wybor&amp;oacute;w i pieniędzy.&lt;/p&gt;
&lt;p&gt;Zanim jednak tym architektem zostaniesz warto odpowiedzieć sobie na te pytanie.&lt;/p&gt;
&lt;p&gt;Oto 6 pytań dla przyszłych architekt&amp;oacute;w oprogramowania.&lt;/p&gt;
&lt;p&gt;[more]&lt;/p&gt;
&lt;h3&gt;1. Czy w programowaniu istnieje srebrna kula ?&lt;/h3&gt;
&lt;p&gt;Nie ma srebrnej kuli w oprogramowaniu. M&amp;oacute;głbyś dojść do wniosku, że po 63 latach po wynalezieniu COBOL-a nasze oprogramowanie powinno dojść do fazy, w kt&amp;oacute;rej istnieje idealne rozwiązanie. Tak jednak nie jest.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Z mojego doświadczenia techniki programistyczne można podzielić na dwie kategorię:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fatalne&lt;/li&gt;
&lt;li&gt;i dobre, ale to zależy. No właśnie "to zależy".&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Warto o tym pamiętać, gdy będziesz miał obsesje nad swoim młotkiem i będziesz nim pr&amp;oacute;bował montować kafelki, bo dla Ciebie wszystko jest teraz gwoździem.&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2. Czy kwestionujesz Hype ?&lt;/h3&gt;
&lt;p&gt;Ten problem dotyka całego obszaru IT i został on nazwany już wieloma śmiesznymi skr&amp;oacute;tami jak:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Hype Driven Development",&lt;/li&gt;
&lt;li&gt;"Conference Driven Development",&lt;/li&gt;
&lt;li&gt;"Loudest Guy Driven Descisions"&lt;/li&gt;
&lt;li&gt;i ostatnie najważniejsze "Resume Driven Development",&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...czyli to jak nasze dążenie do dodawania fajnych nowych technologi do CV wpływa na tworzenie aplikacji w korporacjach.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/architekt-programista-6-pytan-do-przyszłego-ciebie/00-qq_637853689486511158.png" alt="Parodie książek" width="770" height="266" /&gt;&lt;/p&gt;
&lt;p&gt;Każdy chce dodać nową technologię do CV, aby zwiększyć swoją wartość, ale aby to zrobić z czystym sumieniem, to trzeba taką aplikację stworzyć w swojej obecnej firmie. Znaczy to, że marketingowe kręcenie pewnych rozwiązań technologicznych pociąga wiele sznurk&amp;oacute;w w tym trendy w CV i&amp;nbsp; potem te trendy wypluwają nie koniecznie dobre programistyczne twory.&lt;/p&gt;
&lt;p&gt;To, że coś jest nowe, nie znaczy automatycznie, że jest to lepsze. To, że&amp;nbsp; coś jest popularne, nie znaczy, że automatycznie, że jest to perfekcyjne narzędzie, kt&amp;oacute;re rozwiąże tw&amp;oacute;j konkretny problem. Tworzymy oprogramowanie, aby rozwiązywać problemy, a nie po to, aby dopisać sobie technologię do CV.&lt;/p&gt;
&lt;p&gt;Dlatego warto kwestionować każde rozwiązanie, zwłaszcza jeśli to rozwiązanie jest dopiero w swojej fazie "rozgłosu" i nie zostało tak naprawdę sprawdzone na dużą skalę, a to zajmuje zazwyczaj danej technologi 3-4 lata. Nie dziw się, więc jeśli tw&amp;oacute;j architekt nie podziela entuzjazmu twojego nowym rozwiązaniem, bo ma do tego swoje powody.&lt;/p&gt;
&lt;h3&gt;3. Czy kwestionujesz wzorce projektowe ?&lt;/h3&gt;
&lt;p&gt;Gdy zaczynałem programować z 10 lat temu, to byłem przekonany, że wzorce projektowe są kluczem do czystego, czytelnego kodu i znajomość tych wzorcu będzie mi otwierało możliwości zabłyśnięcia na rozmowach kwalifikacyjnych.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Gdy już pracowałem, to kupiłem sobie książkę za 300 zł i myślałem, że tę cenę kupuje wręcz święty Gral programistyczny. Ta książka nie tylko opisywała wszystkie wzorce projektowe z "Gang of Four", ale także pokazała ich użycie w poszczeg&amp;oacute;lnych warstwach aplikacji ASP.NET Web Forms.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/architekt-programista-6-pytan-do-przyszłego-ciebie/00-ks_637853689486764931.png" alt="00-ks.png" width="234" height="312" /&gt;&lt;/p&gt;
&lt;p&gt;Po kr&amp;oacute;tkiej analizie książki doszedłem do wniosku, że połowa tych wzorc&amp;oacute;w projektowych jest albo beznadziejna, albo bardzo sytuacyjna, albo już ktoś wymyślił coś lepszego. Potem lata mijały w mojej karierze programisty i najczęściej używanym wzorcem projektowym był tylko dekorator.&lt;/p&gt;
&lt;p&gt;Mogłem, też zobaczyć jak pewien architekt zrobił bardzo nieczytelny kod, ponieważ użył wzorca projektowego "State", kt&amp;oacute;ry jest w porządku, dop&amp;oacute;ki logika nie jest zarządzana przez 30 oddzielnych klas, kt&amp;oacute;re mają reprezentować, jak sama nazwa wzorca wskazuje, oddzielny stan i akcje dla danego elementu w tym stanie.&lt;/p&gt;
&lt;p&gt;Ja jednak by wolał jeden plik, gdzie mogę wzrokiem zrozumieć, co ja właściwie robię, gdy takie i takie stany danego elementu występują. Niby to antywzorzec, ale przynajmniej bym wiedział, co ja właściwie robię. Teraz też wiem, że dobrą alternatywą dla wzorca "State" jest maszyna stan&amp;oacute;w, kt&amp;oacute;ra nie była opisana w wielu książkach.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To mnie nauczyło kwestionować rozwiązania programistyczne nawet te poparte autorytetem i ceną książki.&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4. Czy znasz prawo Conway'a ?&lt;/h3&gt;
&lt;p&gt;Jako programista zauważyłeś, że ostatnio bardzo stały się popularne mikroserwisy&amp;nbsp; oraz mikro-frontendy. Tak jak m&amp;oacute;wiłem wcześniej to, że coś jest popularne, to automatycznie nie znaczy, że są to rozwiązania dla Ciebie.&lt;/p&gt;
&lt;p&gt;Ich popularność można tłumaczyć "Prawem Conway'a", kt&amp;oacute;re nam m&amp;oacute;wi, że naszego oprogramowanie będzie dążyć do struktury, jaką mamy w naszych zespołach w firmie.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Skoro mamy małe zespoły po 3-5 os&amp;oacute;b to nasze oprogramowanie też chce dążyć do małych aplikacji backend i frontend.&lt;/p&gt;
&lt;p&gt;Nie dlatego, że jest to uniwersalnie dobre, ale dlatego, że takie mamy podziały zespoł&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Prawa Conway's nie da się oszukać. Jest jak prawo grawitacji.&amp;nbsp; Jeśli mamy złą komunikację w zespołach albo struktura zespoł&amp;oacute;w&amp;nbsp; jest jak przyciśnięcie wzajemnie do siebie pistolet&amp;oacute;w do głowy, to te wszystkie problemy wyjdą w oprogramowaniu.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/architekt-programista-6-pytan-do-przyszłego-ciebie/00-ilu_637853689486854176.png" alt="00-ilu.png" width="770" height="433" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;em&gt;Przykład zbyt dużego zespołu. Jak myślisz działa on wydajnie ?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Dlatego architekci nie tylko robią diagramy projekt&amp;oacute;w z kodem, ale także robią mapę kontekst&amp;oacute;w i zespoł&amp;oacute;w, kt&amp;oacute;re w jakiś spos&amp;oacute;b ze sobą muszą się komunikować, aby firma jako cały puzzel lub organizm jakoś funkcjonowała.&lt;/p&gt;
&lt;p&gt;Fachowo m&amp;oacute;wi się na to "Context Map".&lt;/p&gt;
&lt;p&gt;Ciekawe, że można znaleźć problem w oprogramowaniu nie w kodzie, ale w relacjach między zespołami i być może w niedalekiej przyszłości jako architekt twoim zadaniem będzie włożyć kij w mrowisko polityki firmy, aby przyszłe oprogramowania był lepsze.&lt;/p&gt;
&lt;h3&gt;5. Czy to się skaluje ?&lt;/h3&gt;
&lt;p&gt;Gdy już znasz najlepsze z najlepszych praktyk w architekturze w twojej głowie na nie jednym spotkaniu pojawi się pytanie "&lt;strong&gt;Czy to się skaluje ?&lt;/strong&gt;".&lt;/p&gt;
&lt;p&gt;No c&amp;oacute;ż, żeby, to zweryfikować to trzeba napisać prototyp albo mieć już doświadczenie z daną techniką.&lt;/p&gt;
&lt;p&gt;Ostatnio jednak pojawiło się wiele alarm&amp;oacute;w o odwrotnym problemie. Nie każdy z nas będzie tworzył drugiego Facebooka, to znaczy, że wiele genialnych praktyk będzie przerostem formy nad treścią naszego rozwiązania.&lt;/p&gt;
&lt;p&gt;M&amp;oacute;wi się na to " &lt;strong&gt;Overengineering&lt;/strong&gt;".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Dlatego, zamiast zadawać sobie pytanie ", Czy to się skaluje?" warto także zadać sobie pytanie "&lt;strong&gt;A co jeśli tyle mi wystarczy?&lt;/strong&gt;".&lt;/p&gt;
&lt;p&gt;Dodatkowo warto zaznaczyć, że genialna technika to jedno, ale jej dobra implementacja to drugie.&amp;nbsp; Wiesz mi, każdy programista wolałby aplikację CRUD niż źle napisaną aplikację CQRS z Event Sourcing. Dlatego skup się na jak najprostszych rozwiązaniach dopasowanych do skali swojego problemu.&lt;/p&gt;
&lt;h3&gt;6. Czy masz swoje zdanie ?&lt;/h3&gt;
&lt;p&gt;Warto weryfikować każdą technikę i uważać, aby nie stać się papugą, kt&amp;oacute;ra powtarza to, co usłyszała na konferencji albo od innego kolesia, kt&amp;oacute;ry głośnio krzyczy.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Ciągle się uczymy i niestety nic nie zastąpi twojej weryfikacji danej techniki z prawdziwym projektem biznesowym.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;TL;DR Zawsze możesz też obejrzeć filmik na YT na ten temat :)&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;iframe title="YouTube video player" src="https://www.youtube.com/embed/xe5aEvFkoiM" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"&gt;&lt;/iframe&gt;;&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>filozofia</category>
  <category>architektura</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/architekt-programista-6-pytan-do-przyszlego-ciebie</guid>
  <pubDate>Tue, 12 Apr 2022 13:28:15 GMT</pubDate>
</item>
<item>
  <title>Excel VBA : Tworzenie, kopiowanie i nazywanie dynamiczne arkuszy</title>
  <link>https://cezarywalenciuk.pl/blog/programing/excel-vba-tworzenie-kopiowanie-i-nazywanie-dynamiczne-arkuszy</link>
  <description>&lt;p&gt;[metro] Kolejne wyzwanie Visual Basic For Application dla Excela mnie spotkało. Czy można w Excelu dynamicznie wygenerować arkusze, kt&amp;oacute;re są kopią istniejącego arkusza? Oczywiście, że tak.&lt;/p&gt;
&lt;p&gt;Czy można nadać nazwę według pewnego wzoru tym arkuszom? Tak&lt;/p&gt;
&lt;p&gt;Oto zadanie, kt&amp;oacute;re miałem wykonać. Mam więc arkusz do kopiowania wygląda on tak. [more]&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba--tworzenie-i-nazywanie-dynamiczne-arkuszy/arkusz-02_637813016761892648.PNG" alt="Arkusz do kopiowania w Excel 2019" width="341" height="202" /&gt;&lt;/p&gt;
&lt;p&gt;Każda kopia takiego arkusza musi mieć swoją nazwę według pewnego wzoru. Oto moja tabelka.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba--tworzenie-i-nazywanie-dynamiczne-arkuszy/arkusz-02_637813016762473354.PNG" alt="Kod do arkuszy w Excel 2019" width="738" height="255" /&gt;&lt;/p&gt;
&lt;p&gt;Zanim więc przejdziemy do generowania takich arkuszy musi najpierw określić kod nazwy. Jego wz&amp;oacute;r jest następujący&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"4 pierwsze litery z miasta" + "_" + "Ostatnia cyfra z Numer1" + "_" + "Pierwsza cyfra z Numer 2" + "_" + "Kod Regionu"&lt;/p&gt;
&lt;p&gt;Kod VBA, kt&amp;oacute;ry takie napisy wygeneruje wygląda tak:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sub DajKodDoArkuszu()

For x = 2 To 9
    
    Dim cellcity, cellnumber1, cellnumeber2, cellregion As String
    
    cellcity = "B" &amp;amp; x
    cellnumber1 = "C" &amp;amp; x
    cellnumeber2 = "D" &amp;amp; x
    cellregion = "E" &amp;amp; x
    
    Dim vcity, vnumber1, vnumber2, vregion As String
    
    vcity = Range(cellcity).Value
    vnumber1 = Range(cellnumber1).Value
    vnumber2 = Range(cellnumeber2).Value
    vregion = Range(cellregion).Value
       
    Dim lenghtcity
    lenghtcity = Len(vcity)
    
    If lenghtcity &amp;gt; 4 Then
        vcity = Left(vcity, 4)
    End If
    
    vnumber1 = Right(vnumber1, 1)
    vnumber2 = Left(vnumber2, 1)
    
    Dim result As String
    
    result = "cit_" &amp;amp; vcity &amp;amp; "_" &amp;amp; vnumber1 &amp;amp; "_" &amp;amp; vnumber2 &amp;amp; "_" &amp;amp; vregion
    result = LCase(result)
    
    Range("A" &amp;amp; x).Value = result

Next x

End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wbudowana funkcja Len określi mi ilość znak&amp;oacute;w w danym napisie.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Dim lenghtcity
lenghtcity = Len(vcity)

If lenghtcity &amp;gt; 4 Then
    vcity = Left(vcity, 4)
End If&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Korzystając z funkcji Left jestem w stanie wyciągnąć z danego napisu pierwsze 4 znaki.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;vnumber1 = Right(vnumber1, 1)
vnumber2 = Left(vnumber2, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Istnieje też podobna funkcja Right, kt&amp;oacute;ra wyciągnie mi znaki od końca mojego napisu lub numeru.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;result = "cit_" &amp;amp; vcity &amp;amp; "_" &amp;amp; vnumber1 &amp;amp; "_" &amp;amp; vnumber2 &amp;amp; "_" &amp;amp; vregion
result = LCase(result)
Range("A" &amp;amp; x).Value = result&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na koniec pozostało nam te napisy złączyć i umieścić ich zawartość do kolumny "A".&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba--tworzenie-i-nazywanie-dynamiczne-arkuszy/arkusz-04_637813016762515156.PNG" alt="Kod do arkuszy rezultat działania metody VBA" width="722" height="256" /&gt;&lt;/p&gt;
&lt;p&gt;To był fragment naszego zadania. Teraz przechodzimy do generowania arkuszy przy użyciu VBA.&lt;/p&gt;
&lt;p&gt;Cały ten wcześniejszy kod możemy przerobić na funkcje, kt&amp;oacute;ra nam zwr&amp;oacute;ci tablice wygenerowanych napis&amp;oacute;w. Ta tablica będzie nam potrzebna do generacji szablon&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Function DajTabliceKodow() As String()

Dim arr(7) As String
Dim index As Integer
index = 0

For x = 2 To 9
    
    Dim cellcity, cellnumber1, cellnumeber2, cellregion As String
    
    cellcity = "B" &amp;amp; x
    cellnumber1 = "C" &amp;amp; x
    cellnumeber2 = "D" &amp;amp; x
    cellregion = "E" &amp;amp; x
    
    Dim vcity, vnumber1, vnumber2, vregion As String
    
    vcity = Range(cellcity).Value
    vnumber1 = Range(cellnumber1).Value
    vnumber2 = Range(cellnumeber2).Value
    vregion = Range(cellregion).Value
       
    Dim lenghtcity
    lenghtcity = Len(vcity)
    
    If lenghtcity &amp;gt; 4 Then
        vcity = Left(vcity, 4)
    End If
    
    vnumber1 = Right(vnumber1, 1)
    vnumber2 = Left(vnumber2, 1)
    
    Dim result As String
    
    result = "cit_" &amp;amp; vcity &amp;amp; "_" &amp;amp; vnumber1 &amp;amp; "_" &amp;amp; vnumber2 &amp;amp; "_" &amp;amp; vregion
    result = LCase(result)
    
    arr(index) = result
    index = index + 1
    
Next x

DajTabliceKodow = arr

End Function&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do działania będziemy też potrzebować innych pomocniczych funkcji. Potrzebujemy funkcji, kt&amp;oacute;ra zwr&amp;oacute;ci nam wielkość danej tablicy&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Public Function GetArrLength(a As Variant) As Integer
   If IsEmpty(a) Then
      GetArrLength = 0
   Else
      GetArrLength = UBound(a) - LBound(a) + 1
   End If
End Function&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Przyda nam się też funkcja, kt&amp;oacute;ra sprawdzi dla bezpieczeństwa czy nie istnieje już taki arkusz o takiej nazwie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Function Sheet_Exists(WorkSheet_Name As String) As Boolean
Dim Work_sheet As Worksheet
 
Sheet_Exists = False
 
For Each Work_sheet In ThisWorkbook.Worksheets
 
    If Work_sheet.Name = WorkSheet_Name Then
        Sheet_Exists = True
    End If
 
Next
 
End Function&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak można dodać arkusz w VBA? Wystarczy taki fragment kodu&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sheets.Add.Name = "NowyArkusz"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jeśli chcesz dodać arkusz przed lub po jakiś arkuszu to kod trochę ulega zmianie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sheets.Add After:=Sheets("Arkusz1") 'Dodaj po Arkuszu1'
Sheets.Add(After:=Sheets("Arkusz1")).Name = "NowyArkusz" 'Dodaj po Arkuszu1 i zmień nazwe'

Sheets.Add(Before:=Sheets(1)).Name = "FirstSheet 'Dodaj na początku'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nas jednak interesuje kopiowanie i umieszczanie każdego kolejno takiego szablonu na końcu.&lt;/p&gt;
&lt;p&gt;Kod generujący wygląda tak&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sub UtworzArkusze()

Dim arr() As String
arr = DajTabliceKodow()

Dim sizeArray
sizeArray = GetArrLength(arr)

For y = 0 To sizeArray - 1

    Dim Sheet_Name As String
    
    Sheet_Name = arr(y)
    
    If (Sheet_Exists(Sheet_Name) = False) Then
    
        Sheets("ArkuszDoKopiowania").Copy After:=Sheets(Sheets.Count)
        ActiveSheet.Name = Sheet_Name
        
    End If
Next y

End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak widzisz dosyć łatwo jest utworzyć dynamicznie arkusze w VBA w Excelu.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba--tworzenie-i-nazywanie-dynamiczne-arkuszy/arkusz-05_637813016762535465.PNG" alt="arkusz-05.PNG" width="764" height="37" /&gt;&lt;/p&gt;
&lt;p&gt;Najbardziej interesuje cię zapewne ten fragment kodu. Metoda Copy utworzy kopię, a funkcja After ustawi nowo wygenerowany tak szablon na samym końcu.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sheets("ArkuszDoKopiowania").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = Sheet_Name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gdy ten szablon się generuje to jest on w tym momencie aktywnym szablonem.&amp;nbsp; To daje nam możliwość odwołania się do niego i wtedy mam szanse zmienić jego nazwę.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba--tworzenie-i-nazywanie-dynamiczne-arkuszy/arkusz-03_637813034516713707.PNG" alt="Uruchamianie makra w Excel 2019 " width="495" height="443" /&gt;&lt;/p&gt;
&lt;p&gt;To wszystko. Pamiętaj tylko o tym, aby aktywować/zaznaczyć odpowiedni arkusz, gdy uruchamiasz takie makro w VBA.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>excel-vba</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/excel-vba-tworzenie-kopiowanie-i-nazywanie-dynamiczne-arkuszy</guid>
  <pubDate>Thu, 24 Feb 2022 12:14:36 GMT</pubDate>
</item>
<item>
  <title>Excel VBA : Dodanie formuły, która sumuje wartości z arkuszy</title>
  <link>https://cezarywalenciuk.pl/blog/programing/excel-vba-dodanie-formuly-ktora-sumuje-wartosci-z-arkuszy</link>
  <description>&lt;p&gt;[metro] O ile ten blog jest gł&amp;oacute;wnie na temat C#, to czasem pojawiają się wyzwania, kt&amp;oacute;re sprawdzają jak szybko jestem w stanie rozwiązać jakiś problem, gdy o danym języku programowania lub narzędziu nie wiele wiem.&lt;/p&gt;
&lt;p&gt;Dzisiaj dostałem takie wyzwanie związane z Excelem. Czy można w Excelu napisać makro w VBA, kt&amp;oacute;re za Ciebie wygeneruje wyrażenie "SUMA=" i do niego doklei za Ciebie wszystkie arkusze, jakie masz w danym zeszycie?&lt;/p&gt;
&lt;p&gt;Oczywiście, że się da. Po napisaniu Makra w VBA nawet zdałem sobie sprawę, że ten problem można rozwiązać zapewne lepiej przy pomocy menadżera nazw oraz potęgi klawisza SHIFT do zaznaczania wielu arkuszy.&lt;/p&gt;
&lt;p&gt;Ja jednak ponieważ jestem programistą to pokaże Ci jak szybko od podstaw&amp;nbsp; można nauczyć się pisania makr VBA&amp;nbsp; Excelu, tak jak ja to zrobiłem w 15 minut.[more]&lt;/p&gt;
&lt;p&gt;Na początku w programie Excel musisz dodać do wstążki całą zakładkę Deweloper. Bez niej nic nie zrobisz.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/excel-01_637812071322352890.png" alt="Dostosowanie wstążki w Excel 2019 aby dodać możliwość Visual Basic" width="779" height="465" /&gt;&lt;/p&gt;
&lt;p&gt;Następnie z tego nowego pola we wstążce wybierz "Visual Basic".&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/excel-05_637812133915395918.png" alt="Przycisk Visual Basic w wstążce w programie Excel 2019" width="766" height="163" /&gt;&lt;/p&gt;
&lt;p&gt;W tym oknie teraz możesz deklarować nowe moduły. Do nich będziemy dodawać kod. Ten kod będziemy potem uruchamiać w specyficznym arkuszu w Excelu.&lt;/p&gt;
&lt;p&gt;Dodawanie modułu wygląda tak&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/excel-04_637812133915523482.png" alt="Dodawanie module z kodem VBA w programie Microsoft Visual Basic for Applications" width="552" height="461" /&gt;&lt;/p&gt;
&lt;p&gt;Pora napisać naszą pierwszą metodę w naszym module. Sub deklaruje nam funkcje o nazwie "GenerowanieSumy1".&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs vbnet "&gt;Sub GenerowanieSumy1()
    Dim x, Formula
    Formula = "=SUM(" 'Formuła zaczyna się od =SUM('
    
    For x = 1 To (Sheets.Count - 1)
        Formula = Formula &amp;amp; Sheets(x).Name &amp;amp; "!A1," 'Dodaj Nazwę Arkusza i Kom&amp;oacute;rkę i Przecinek'
    Next x
    
    Formula = Left(Formula, Len(Formula) - 1) &amp;amp; ")" 'Usuwanie przecinka i dodawanie nawias&amp;oacute;w'
    Range("A1").Formula = Formula 'Gdzie chcesz umieścić tą formułę?'
End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;"Dim" oznacza deklarację zmiennej o danej nazwie. Potem w pętli FOR przejdę po wszystkich arkuszach opr&amp;oacute;cz ostatniego stąd -1 i wygeneruje się odpowiedni ciąg do formuły. Będzie to wyglądać ostatecznie tak:&lt;/p&gt;
&lt;p&gt;=SUMA(Arkusz1!A1;Arkusz2!A1;Arkusz3!A1;Arkusz4!A1)&lt;/p&gt;
&lt;p&gt;Gdzie przykładowo "akrusz5" będzie arkuszem ostatnim i do niego będę chciał dodać tą wygenerowaną formułę przez VBA. Na razie napisałem na sztywno kom&amp;oacute;rkę "A1". Zobaczymy co możemy z tym zrobić dalej.&lt;/p&gt;
&lt;p&gt;Jak tej metody użyć.&lt;/p&gt;
&lt;p&gt;Wchodzimy na "arkusz5" czyli nasz ostatni arkusz i wciskamy ten guzik.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/excel-065_637812133915604518.png" alt="Przycisk makra w wstążce w programie Excel 2019" width="766" height="163" /&gt;&lt;/p&gt;
&lt;p&gt;Teraz pojawi Ci się okno ze wszystkimi dostępnymi makrami. Ja mam ich więcej, ponieważ już dodałem do swojego modułu kolejne metody.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/excel-07_637812133915674365.PNG" alt="Wybieramy swoje makro w oknie" width="378" height="377" /&gt;&lt;/p&gt;
&lt;p&gt;Oto jak nasze makro działa w praktyce.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/GIF 23.02.2022 12-25-40_637812133915929795.gif" alt="Pokaz naszej funkcji w programie Excel 2019" width="629" height="316" /&gt;&lt;/p&gt;
&lt;p&gt;Oczywiście jest jeden problem z naszym makrem.&lt;/p&gt;
&lt;p&gt;Na sztywno do niego dodaliśmy kom&amp;oacute;rkę A1.&lt;/p&gt;
&lt;p&gt;Co, jeśli chcielibyśmy tak generować sumy dla każdej kom&amp;oacute;rki?&lt;/p&gt;
&lt;p&gt;Możemy przerobić naszą metodę VBA tak, aby przyjmowała ona informacje o kom&amp;oacute;rce jako napis String&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet  "&gt;Sub GenerowanieSumyZParametrem(CELL As String)&lt;br /&gt;Dim x, Formula
Formula = "=SUM(" 'Formuła zaczyna się od =SUM('
For x = 1 To (Sheets.Count - 1)
    Formula = Formula &amp;amp; Sheets(x).Name &amp;amp; "!" &amp;amp; CELL &amp;amp; "," 'Dodaj Nazwę Arkusza i Kom&amp;oacute;rkę i Przecinek'
Next x
Formula = Left(Formula, Len(Formula) - 1) &amp;amp; ")" 'Usuwanie przecinka i dodawanie nawias&amp;oacute;w'
Range(CELL).Formula = Formula 'Gdzie chcesz umieścić tą formułę?'
End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Niestety nie ma możliwości uruchomienia makra z parametrem bez tworzenia do niego jakieś formatki z przyciskiem.&lt;/p&gt;
&lt;p&gt;Tworzenie też takich funkcji per kom&amp;oacute;rka, też mija się z celem. Warto zaznaczyć, że jak uruchamiasz funkcje wewnątrz innej funkcji to najpierw musisz podać nazwę modułu tej funkcji. U mnie moduł nazywa się "GenerowanieSumy".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Przeciwnym wypadku dostaniesz błąd : Expected Variable or Procedure, not Module.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet "&gt;Sub GenerowanieSumyA2()
    GenerowanieSumy.GenerowanieSumyZParametrem("A2")&lt;br /&gt;End Sub

Sub GenerowanieSumyA1()
    Call GenerowanieSumy.GenerowanieSumyZParametrem(CELL:="A1")&lt;br /&gt;End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na szczęście do problemu można podejść bardzo prymitywnie, ale też i skutecznie.&lt;/p&gt;
&lt;p&gt;Przykładowo chce mieć sumy wartości kom&amp;oacute;rek od C3 do N40. Jak to zrobić? Utworzyłem sobie tablice "CellsName", kt&amp;oacute;ra zawiera nazwy kom&amp;oacute;rek od C do N. Chociaż zaraz Ci pokaże jak to zrobić bez tworzenia takiej tablic.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet  "&gt;Sub GenerowanieSumyC3toN40()
    
    Dim CellsName(1 To 12) As String
    CellsName(1) = "C"
    CellsName(2) = "D"
    CellsName(3) = "E"
    CellsName(4) = "F"
    CellsName(5) = "G"
    CellsName(6) = "H"
    CellsName(7) = "I"
    CellsName(8) = "J"
    CellsName(9) = "K"
    CellsName(10) = "L"
    CellsName(11) = "M"
    CellsName(12) = "N"
       
    For x = 3 To 40
        For y = 1 To 12
             Dim CELL As String
             CELL = CellsName(y) &amp;amp; x
             
             Call GenerowanieSumy.GenerowanieSumyZParametrem(CELL)&lt;br /&gt;
        Next y
    Next x

End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Potem wykonuje pętle w pętli i tak dla każdej kom&amp;oacute;rki od "C3 do N40" wykonam funkcję "GenerowanieSumyZParametrem".&lt;/p&gt;
&lt;p&gt;Oto rezultat działanie tej metody&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/excel-vba-dodanie-formuły-ktora-sumuje-wartosci-z-arkuszy/GIF 23.02.2022 12-39-04_637812133916463562.gif" alt="Tworzenie sumy dla wielu kom&amp;oacute;rek dzięki VBA" width="629" height="737" /&gt;&lt;/p&gt;
&lt;p&gt;Jak widzisz funkcje nie są skomplikowane. Nawet taki prymitywnym podejściem można rozwiązać skomplikowany problem.&lt;/p&gt;
&lt;p&gt;Visual Basic for Applications nie jest aż taki straszny&lt;/p&gt;
&lt;p&gt;Czy można napisać ten kod inaczej? Mały tutorial. Oto jak możesz ustawić wartość kom&amp;oacute;rki A2.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  "&gt;Cells(2,1).Value = 1
Range("A2").Value = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W metodzie Cells w drugim parametrze podajesz kolumnę w formacie cyfrowej. Te dwie linki kodu robią dokładnie to samo&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet  "&gt;Cells("A1").Formula = Formula 'Gdzie chcesz umieścić tą formułę?'
Cells(1, 1).Formula = Formula 'Gdzie chcesz umieścić tą formułę?'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wiedząc to możemy teraz zmodyfikować funkcje, kt&amp;oacute;ra będzie generować te formuły. Jednakże do tego problemu możemy podejść jeszcze inaczej bez modyfikowania istniejącej już funkcji, kt&amp;oacute;ra tworzy nam formułę&lt;/p&gt;
&lt;p&gt;Do szczęścia potrzebujemy pomocniczej funkcję, kt&amp;oacute;ra zamieni nam zapis cyfrowy kolumny na napis alfabetyczny.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet  "&gt;Function Col_Letter(lngCol) As String
    Dim vArr
    vArr = Split(Cells(1, lngCol).Address(True, False), "$")
    Col_Letter = vArr(0)
End Function&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dzięki temu teraz stworzyć lepszą wersję metody, kt&amp;oacute;ra zadziała dla kom&amp;oacute;rek od C3 do N40.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  vbnet  "&gt;Sub GenerowanieSumy2C3toN40()
    
    For x = 3 To 40
        For y = 1 To 12
             ColumnLetter = GenerowanieSumy.Col_Letter(y)
            
             Dim CELL As String
             CELL = ColumnLetter &amp;amp; CStr(x)
             Call GenerowanieSumy.GenerowanieSumyWithParameter(CELL)

        Next y
    Next x

End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wszystko.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>excel-vba</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/excel-vba-dodanie-formuly-ktora-sumuje-wartosci-z-arkuszy</guid>
  <pubDate>Wed, 23 Feb 2022 09:58:52 GMT</pubDate>
</item>
<item>
  <title>ImageGlass : Najlepsze przeglądanie zdjęć</title>
  <link>https://cezarywalenciuk.pl/blog/programing/imageglass-spider-najlepsze-przegladanie-zdjec</link>
  <description>&lt;p&gt;[metro] Nie podoba Ci się systemowa przeglądarka zdjęć w systemie Windows. Witaj w klubie. Są rzeczy, kt&amp;oacute;re mnie denerwują w domyślnej przeglądarce zdjęć jak brak możliwości przejścia do następnego zdjęcia, gdy mam powiększone obecne.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Brakuje mi też możliwość szybkiego obrotu poziomowego/pionowego zdjęcia oraz możliwości wycinania interesującego mnie fragmentu.&lt;/p&gt;
&lt;p&gt;To nie są zaawansowane funkcje, ale nie chce otwierać edytora graficznego ("Paint.NET") , aby zrobić takie podstawowe operacje.&lt;/p&gt;
&lt;p&gt;Pr&amp;oacute;bowałem znaleźć program, kt&amp;oacute;ry by mnie zadowolił.&lt;/p&gt;
&lt;p&gt;InfraView to prawie jest to co chce, ale irytują mnie jego ikony, kt&amp;oacute;re są potem podpięte do każdego pliku z obrazkiem. Taki mam z nim problem chyba od 2008 roku.&lt;/p&gt;
&lt;p&gt;Wiele innych program&amp;oacute;w odpadło, ponieważ mają one też sporą listę rzeczy, kt&amp;oacute;rych &lt;strong&gt;nie chce mieć w takim programie&lt;/strong&gt;. [more]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dodatkowych pod okien
&lt;ul&gt;
&lt;li&gt;Z informacją o tym, w jakim folderze obecnie się znajduje&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Zbyt rażących ikon&lt;/li&gt;
&lt;li&gt;Zbyt wielu przycisk&amp;oacute;w, z kt&amp;oacute;rych i tak nie skorzystam&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Śmiechem, żartem kiedyś w akcji desperacji wygrzebałem program ACDSee Viewer, z kt&amp;oacute;rego korzystał jeszcze m&amp;oacute;j brat, gdy miał Windows 98. Potem jednak go zgubiłem po kt&amp;oacute;rymś formacie systemu.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Postanowiłem dać szanse programowi&amp;nbsp; ImageGlass i sprawdziłem, czy spełnia on dla mnie wszystkie te warunki.&lt;/p&gt;
&lt;p&gt;Oto jak wygląda przeglądanie na nim pliku przezroczystych plik&amp;oacute;w .png&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/imageglass-spider--najlepsze-przegladanie-zdjec/program-01_637810411847213907.png" alt="Działanie programu ImageGlass" width="760" height="530" /&gt;&lt;/p&gt;
&lt;p&gt;Warto zaznaczyć, że tę karuzelę (wszystkich obrazk&amp;oacute;w w danym folderze) na dole można schować. Domyślnie jej nie ma.&lt;/p&gt;
&lt;p&gt;Jest tylko obrazek. Powiększanie i przechodzenie do następnej ilustracji żaden spos&amp;oacute;b siebie nie blokuje.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Gdy robiłem skany swoich notatek to sprawdziłem jak wygodnie jest obracać o 90 stopni obrazki i zapisać&amp;nbsp; te wprowadzone zmiany bez otwierania kolejnych edytor&amp;oacute;w graficznych. To samo dotyczy się wycinania.&lt;/p&gt;
&lt;p&gt;Oficjalnie ten program dołączam do swoich program&amp;oacute;w. Program można pobrać tutaj &lt;a href="https://imageglass.org/" target="_blank" rel="noopener"&gt;https://imageglass.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Jakieś wady programu. Najlepiej przegląda się zdjęcia w trybie pełno ekranowym, gdyż powiększanie w trybie okna powoduje zmianę wielkość okna programu. Te zmiany wielkości dla niekt&amp;oacute;rych może wyglądać jak migotanie. Nie wiem, czy jakiś inny program może obejść ten problem, czyli zmieniać sw&amp;oacute;j rozmiar tak szybko, że nie widać tej operacji na monitorze.&lt;/p&gt;
&lt;p&gt;Kolejna wada programu też polega na tym, że mimo iż istnieje w nim tryb "pokaz&amp;oacute;w slajd&amp;oacute;w" to nigdzie w opcjach nie widzę możliwość ustawienia co ile sekund ma się zmienić następny obrazek.&lt;/p&gt;
&lt;p&gt;Jeśli nie podobają Ci się ikony programu to możesz to zmienić instalując odpowiedni Theme. Mnie akurat te szare ikony się podobają.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Gdybyś chciał zmienić skr&amp;oacute;ty klawiszowe programu, jak i myszki to też masz taką możliwość,&lt;/p&gt;
&lt;p&gt;A jak wyglądają ikony tego programu?&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/imageglass-spider-najlepsze-przegladanie-zdjec/program-02_637810424573411414.PNG" alt="imageglass icons are cool" width="243" height="197" /&gt;&lt;/p&gt;
&lt;p&gt;Jak widzisz ikony odpowiednio oznaczają typy obrazk&amp;oacute;w. Jeśli więc potrzebujesz prostej przeglądarki do zdjęć to polecam ten program.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>triki-z-windows</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/imageglass-spider-najlepsze-przegladanie-zdjec</guid>
  <pubDate>Mon, 21 Feb 2022 11:39:54 GMT</pubDate>
</item>
<item>
  <title>Visitor, Odwiedzający : Wzorce projektowe C#</title>
  <link>https://cezarywalenciuk.pl/blog/programing/visitor-odwiedzajacy-wzorce-projektowe-c</link>
  <description>&lt;p&gt;[metro] Drogi czytelniku om&amp;oacute;wiliśmy prawie wszystkie wzorce projektowe z "&lt;strong&gt;Gang of Four&lt;/strong&gt;". Do skończenia tej kolekcji wpis&amp;oacute;w pozostało nam om&amp;oacute;wić ostatni wzorzec projektowy, a jest nim wzorzec projektowy "&lt;strong&gt;Visitor&lt;/strong&gt;".&lt;/p&gt;
&lt;p&gt;Jak najlepiej wyjaśnić ten wzorzec?&lt;/p&gt;
&lt;p&gt;Najlepiej jest od razu przeskoczyć do przykładu.&lt;/p&gt;
&lt;p&gt;Powiedzmy, że mamy następujące wyrażenie matematyczne, kt&amp;oacute;re dla ułatwienia składa się tylko z liczb (możliwie ułamkowych) i operatora odejmowania.&lt;/p&gt;
&lt;p&gt;Oto przykład takiego wyrażenia : &lt;strong&gt;(1.0 - (2.0 - 3.0))&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Chcemy teraz zapisać te wyrażenie matematyczne w spos&amp;oacute;b obiektowy. [more]&lt;/p&gt;
&lt;p&gt;Na początku stw&amp;oacute;rzmy klasę abstrakcyjną, kt&amp;oacute;ra będzie nam określała wszystkie możliwe wyrażenia.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public abstract class Expression {  } 
//na razie puste&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz tworzymy klasę, kt&amp;oacute;ra reprezentuje wyrażenie liczbowe.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class DoubleExpression : Expression
{
    private double value;
    public DoubleExpression(double value) { this.value = value; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto klasa, kt&amp;oacute;ra określa wyrażenie odejmowania. Przyjmuje ona wartości po lewej i po prawej stronie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SubtractionExpression : Expression
{
    private Expression left, right;
    public SubtractionExpression
        (Expression left, Expression right)
    {
        this.left = left;
        this.right = right;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wiemy, do czego chcemy dążyć. Interesują nasz teraz dwie rzeczy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jak wydrukować te wyrażenie obiektowe jako tekst?&lt;/li&gt;
&lt;li&gt;Jak wykonać dane wyrażenie&amp;nbsp; obiektowe, aby otrzymać jego wynik&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Istnieje wiele sposob&amp;oacute;w na rozwiązanie tych problem&amp;oacute;w.&lt;/p&gt;
&lt;h3&gt;Natrętny odwiedzający inaczej&amp;nbsp;Intrusive Visitor&lt;/h3&gt;
&lt;p&gt;Najłatwiej by było dodać metodę drukującą do każdego wyrażenia. Tworząc metodę abstrakcyjną wymusimy takie zachowanie na każdym wyrażeniu zapisanym przy pomocy klas.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public abstract class Expression 
{
    public abstract void Print(StringBuilder sb);
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pozostało nam teraz do klas dodać implementacje tej metod.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class DoubleExpression : Expression
{
    public override void Print(StringBuilder sb)
    {
        sb.Append(value.ToString());
    }

    private double value;
    public DoubleExpression(double value) { this.value = value; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rekurencyjnie i poliformicznie wywołamy metodę "Print()" kt&amp;oacute;ra ostatecznie stworzy nam kompletne wyrażenie w formie tekstowej.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SubtractionExpression : Expression
{
    public override void Print(StringBuilder sb)
    {
        sb.Append(value: "(");
        left.Print(sb);
        sb.Append(value: "-");
        right.Print(sb);
        sb.Append(value: ")");
    }

    private Expression left, right;
    public SubtractionExpression
        (Expression left, Expression right)
    {
        this.left = left;
        this.right = right;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sprawdźmy jak nasz kod działa.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var expression = new SubtractionExpression(
    left: new DoubleExpression(1),
    right: new SubtractionExpression(
    left: new DoubleExpression(2),
    right: new DoubleExpression(3)));

var sb = new StringBuilder();
expression.Print(sb);
Console.WriteLine(sb);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wydaje się to łatwe, ale wyobraź sobie, że w tej hierarchii wyrażenia masz z 10 klas, kt&amp;oacute;re albo dziedziczą po sobie, albo są mniejszą częścią większego wyrażenia. Każda modyfikacji w tych klasach może wywołać reakcję łańcuchową, kt&amp;oacute;ra przejdzie na wszystkie inne klasy co łamię zasadę "Otwarte-Zamknięte"&amp;nbsp; .&lt;/p&gt;
&lt;p&gt;To jednak niejedyny problem&lt;/p&gt;
&lt;p&gt;Kolejnym problemem jest złamana zasada "Pojedynczej odpowiedzialności" . W sumie każda klasa teraz&amp;nbsp; odpowiada za drukowanie całego wyrażenia.&lt;/p&gt;
&lt;p&gt;Powinniśmy przedstawić oddzielną klasę, kt&amp;oacute;ra skupi się tylko na drukowaniu. Potem też możemy stworzyć oddzielną klasę, kt&amp;oacute;ra skupi się na rozwiązaniu całego wyrażenia.&lt;/p&gt;
&lt;h3&gt;Drukarka w stylu Reflective&lt;/h3&gt;
&lt;p&gt;Mamy więc swoje wyrażenie w postaci klasy abstrakcyjnej. Tym razem ona nic w sobie nie zawiera.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public abstract class Expression2
{

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasze klasy wyglądają tak samo jak w pierwszym przykładzie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SubtractionExpression : Expression2
{
    public Expression2  Right {get;set;}

    public Expression2 Left { get; set; }

    public SubtractionExpression
        (Expression2 left, Expression2 right)
    {
        this.Left = left;
        this.Right = right;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class DoubleExpression : Expression2
{
    public double Value { get; set; }
    public DoubleExpression(double value) { this.Value = value; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gdybyś chciał napisać swoją implementację drukowania to zapewne zrobiłbyś to tak&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public static class ExpressionPrinter
{
    public static void Print(DoubleExpression e, StringBuilder sb)
    {
        sb.Append(e.Value);
    }
    public static void Print(SubtractionExpression ae, StringBuilder sb)
    {
        sb.Append("(");
        Print(ae.Left, sb); // to się nie kompiluje
        sb.Append("-");
        Print(ae.Right, sb); // to się nie kompiluje
        sb.Append(")");
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aby jednak ten kod działał musielibyśmy ustalić, że wewnątrz wyrażenia odejmowania zawsze będą liczby ułamkowe. Co, jeśli będę chciał rozwijać sw&amp;oacute;j program i będę tam w tym wyrażeniu odejmowania umieszczał jeszcze inne wyrażenia.&lt;/p&gt;
&lt;p&gt;Nie możemy więc zmodyfikować klasy "SubtractionExpression"&lt;/p&gt;
&lt;p&gt;Możemy stworzyć metodę drukującą wszystko i w niej będziemy sprawdzać, z jakim typem wyrażenia mamy do czynienia.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public static class ExpressionPrinter
{
    public static void Print(Expression2 e, StringBuilder sb)
    {
        if (e is DoubleExpression de)
        {
            sb.Append(de.Value);
        }
        else if (e is SubtractionExpression ae)
        {
            sb.Append("(");
            Print(ae.Left, sb);
            sb.Append("+");
            Print(ae.Right, sb);
            sb.Append(")");
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jest to jakieś rozwiązanie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var expression = new SubtractionExpression(
    left: new DoubleExpression(1),
    right: new SubtractionExpression(
    left: new DoubleExpression(2),
    right: new DoubleExpression(3)));

var sb = new StringBuilder();
ExpressionPrinter.Print(expression, sb);
Console.WriteLine(sb);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Te rozwiązanie oczywiście ma swoją wadę. Bez użycia refleksji w sumie nie masz jak sprawdzić, czy wszystkie możliwe warunki if-else dla wszystkich typ&amp;oacute;w wyrażenia w programie biorą udział w drukowaniu.&lt;/p&gt;
&lt;p&gt;Jeśli dojdzie nowe wyrażenie to oczywiście klasę "ExpressionPrinter" musisz zmodyfikować.&lt;/p&gt;
&lt;p&gt;Czy można to zrobić lepiej?&lt;/p&gt;
&lt;h3&gt;Dynamiczny Odwiedzający : Dynamic Visitor&lt;/h3&gt;
&lt;p&gt;Na ratunek przychodzi słowo kluczowe "&lt;strong&gt;dynamic&lt;/strong&gt;", kt&amp;oacute;re sprawia, że dany fragment kodu&amp;nbsp; zostanie sprawdzony, dopiero gdy ten kod się uruchomi.&lt;/p&gt;
&lt;p&gt;Tradycyjnie sprawdzanie typ&amp;oacute;w odbywa się w trakcie kompilacji programu.&lt;/p&gt;
&lt;p&gt;Ten fragment kodu działa.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ExpressionPrinter2
{
    public void Print(SubtractionExpression se, StringBuilder sb)
    {
        sb.Append("(");
        Print((dynamic)se.Left, sb);
        sb.Append("+");
        Print((dynamic)se.Right, sb);
        sb.Append(")");
    }

    public void Print(DoubleExpression de, StringBuilder sb)
    {
        sb.Append(de.Value);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dalej już nic nie musimy zmieniać.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var sb = new StringBuilder();
ExpressionPrinter2 expressionPrinter2 
    = new ExpressionPrinter2();

expressionPrinter2.Print(expression, sb);

Console.WriteLine(sb);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jakie są problemy z tym rozwiązaniem ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Program działa trochę wolniej niż wcześniej wynika to z działania kodu spos&amp;oacute;b dynamiczny&lt;/li&gt;
&lt;li&gt;Jeśli danej metody lub właściwości nie ma to oczywiście dostaniesz wyjątek RunTime Exception&lt;/li&gt;
&lt;li&gt;Mogą pojawić się problemy, gdy jeszcze nam dojdzie mechanizm dziedziczenia&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dynamiczny odwiedzający ma sens, gdy wiesz, że twoje wyrażenia nie będą aż tak skomplikowane oraz gdy wiesz, że dana metoda jak "Print" nie będzie wywoływana za często.&lt;/p&gt;
&lt;p&gt;Przeciwnym wypadku tw&amp;oacute;j system może mieć zbyt duże czkawki.&lt;/p&gt;
&lt;h3&gt;Klasyczny Odwiedzający : Classic Visitor&lt;/h3&gt;
&lt;p&gt;Jak wygląda klasyczne podejście do wzorca "Visitor". Ot&amp;oacute;ż potrzebne są następujące metody&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metoda Visit() wykona operacje wydruku albo liczenia. Nie chcemy mieć tej metod w każdym elemencie wyrażenie. Trzeba to jakoś wydzielić i zaraz Ci pokaże jak&lt;/li&gt;
&lt;li&gt;Metoda Accept() do sprawdzenia, czy metoda Visit() powinna się uruchomić. Ta metoda powinna się znajdować w każdym elemencie wyrażenia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tworzymy więc kolejną wersję naszej klasy abstrakcyjnej.&lt;/p&gt;
&lt;p&gt;Tym razem każde wyrażenie musi mieć metodę "Accept()", kt&amp;oacute;ra przyjmuje do siebie klasę, kt&amp;oacute;ra będzie implementować interfejs "IExpressionVisitor".&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public abstract class Expression3
{
    public abstract void Accept(IExpressionVisitor visitor);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ten interfejs będzie miał w swoim kontrakcie wszystkie metody odwiedzające dla każdego wyrażenia.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IExpressionVisitor
{
    void Visit(DoubleExpression de);
    void Visit(SubtractionExpression se);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto użycie naszej klasy abstrakcyjnej&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SubtractionExpression : Expression3
{
    public Expression3 Right { get; set; }

    public Expression3 Left { get; set; }

    public SubtractionExpression
        (Expression3 left, Expression3 right)
    {
        this.Left = left;
        this.Right = right;
    }

    public override void Accept(IExpressionVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class DoubleExpression : Expression3
{
    public double Value { get; set; }
    public DoubleExpression(double value) { this.Value = value; }

    public override void Accept(IExpressionVisitor visitor)
    {
        visitor.Visit(this);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O ile kod się powtarza teraz to też mamy możliwość dodania logiki blokującej wizytę danego elementu.&lt;/p&gt;
&lt;p&gt;Teraz pozostało napisać nam trzecią wersję naszej drukarki, kt&amp;oacute;ra będzie implementować interfejs "IExpressionVisitor"&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ExpressionPrinter3 : IExpressionVisitor
{
    StringBuilder sb = new StringBuilder();

    public void Visit(DoubleExpression de)
    {
        sb.Append(de.Value);
    }
    public void Visit(SubtractionExpression se)
    {
        sb.Append("(");
        se.Left.Accept(this);
        sb.Append("+");
        se.Right.Accept(this);
        sb.Append(")");
    }
    public override string ToString() =&amp;gt; sb.ToString();

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nie musimy też teraz korzystać ze słowa kluczowego dynamic, dzięki potędze słowa "this", kt&amp;oacute;ra przekaże instancje naszej drukarki do innych metod.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var expression = new SubtractionExpression(
new DoubleExpression(1),
new SubtractionExpression(
new DoubleExpression(2),
new DoubleExpression(3)));

var ep = new ExpressionPrinter3();
Console.WriteLine($"{ep}");&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visitor / Odwiedzający do kalkulacji matematycznej&lt;/h3&gt;
&lt;p&gt;Zrobiliśmy przed chwilą drukowanie, a co z liczeniem danego wyrażenia? Nic nie stoi na przeszkodzie, aby napisać innego odwiedzającego, kt&amp;oacute;ry właśnie to zrobi.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ExpressionCalculator : IExpressionVisitor
{
    public double Result;

    public void Visit(DoubleExpression de)
    {
        Result = de.Value;
    }
    public void Visit(SubtractionExpression se)
    {
        //za chwile
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dla odejmowania zrobimy operacje odejmowania. Metoda Visit niczego nigdy nie powinna zwracać więc rezultat odejmowania idzie do innej właściwości.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public void Visit(SubtractionExpression se)
{
    se.Left.Accept(this);
    var a = Result;
    se.Right.Accept(this);
    var b = Result;
    Result = a - b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oto przykład naszego odwiedzającego, kt&amp;oacute;ry umie interpretować wyrażenia matematyczne.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var expression = new SubtractionExpression(
new DoubleExpression(1),
new SubtractionExpression(
new DoubleExpression(2),
new DoubleExpression(3)));

var ep = new ExpressionPrinter3();

var calc = new ExpressionCalculator();
calc.Visit(expression);
Console.WriteLine($"{ep} = {calc.Result}");&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz możesz stworzyć według tego wzoru kolejnych odwiedzających, kt&amp;oacute;rzy będą operować na tych samym cegiełkach danego wyrażenia.&lt;/p&gt;
&lt;p&gt;Co ciekawe każdy nowy odwiedzający nie będzie robił zmian w tych klasach reprezentujących dany elementy wyrażenia.&lt;/p&gt;
&lt;p&gt;W ten spos&amp;oacute;b wiemy ,że zasada "Otwarte-Zamknięte" oraz zasada "Pojedynczej odpowiedzialności" jest spełniona. Co czyni kod czytelniejszym&amp;nbsp; i łatwym do zrozumienia.&lt;/p&gt;
&lt;h3&gt;Acykliczny Odwiedzający : Acyclic Visitor&lt;/h3&gt;
&lt;p&gt;Wzorzec projektowy Visitor, czyli odwiedzający ma jeszcze swoje dwa odłamy.&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cykliczny odwiedzający : polega na technice przeciążania metody/funkcji. Z tej techniki skorzystaliśmy do tej pory. Jednakże ten styl ma sens, gdy mamy styczność z hierarchią, kt&amp;oacute;ra jest stabilna i się nie zmienia.&lt;/li&gt;
&lt;li&gt;Acykliczny odwiedzający : polega na rzutowaniu. Limitacje związane ze znajomością typu odwiedzającego znikają, ale idzie za tym koszt wydajności.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zobaczmy jak możemy napisać Acyklicznego odwiedzającego:&lt;/p&gt;
&lt;p&gt;W naszej drukarce nie potrzebujemy już metody Visit dla każdego typu. Możemy zrobić jedną metodę Visit, kt&amp;oacute;ra rozmnoży się na inne metody generyczne w zależności od typu wyrażenia.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IVisitor&amp;lt;TVisitable&amp;gt;
{
    void Visit(TVisitable obj);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Potrzebujemy też pusty interfejs, kt&amp;oacute;ry określi nam to, że działamy ze wzorcem odwiedzającego. Poza oznaczeniem ten Interfejs nie robi nic.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IVisitor { }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tworzymy czwartą wersję klasy abstrakcyjne dla wszystkich wyrażeń. Jak widzisz poniższa metoda Accept jest wirtualna, a nie abstrakcyjna. Znaczy, to ,że patrzymy na domyślną implementację tej metody, chociaż możemy to zmienić poprzez przeciążenie tej metody&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public abstract class Expression4
{
    public virtual void Accept(IVisitor visitor)
    {
        if (visitor is IVisitor&amp;lt;Expression4&amp;gt; typed)
            typed.Visit(this);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Co tutaj się dzieje w tej nowej metodzie Accept ? Nasz parametr w tej metodzie musi implementować interfejs og&amp;oacute;lny &lt;strong&gt;IVisitor&lt;/strong&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;P&amp;oacute;źniej ten parametr spr&amp;oacute;bujemy z rzutować na interfejs IVisitor&amp;lt;T&amp;gt;, gdzie T będzie obecnym typem, w kt&amp;oacute;rym się znajdujemy.&lt;/p&gt;
&lt;p&gt;Jeśli rzutowanie wykona się poprawnie wtedy wywołamy na tym typie metodę Visit().&lt;/p&gt;
&lt;p&gt;Nasze elementy wyglądają tak samo. Pamiętaj, że każdy z nich ma tę domyślną implementację metody Visit() z możliwością przeciążenia.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class SubtractionExpression : Expression4
{
    public Expression4 Right { get; set; }

    public Expression4 Left { get; set; }

    public SubtractionExpression
        (Expression4 left, Expression4 right)
    {
        this.Left = left;
        this.Right = right;
    }

}

public class DoubleExpression : Expression4
{
    public double Value { get; set; }
    public DoubleExpression(double value) { this.Value = value; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do sklejenia tego podejścia "Acyklicznego" potrzebujmy jeszcze drukarki, kt&amp;oacute;ra nam wyświetli te wyrażenie w formie tekstowej.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ExpressionPrinter : IVisitor,
IVisitor&amp;lt;Expression4&amp;gt;,
IVisitor&amp;lt;DoubleExpression&amp;gt;,
IVisitor&amp;lt;SubtractionExpression&amp;gt;
{
    StringBuilder sb = new StringBuilder();
    public void Visit(DoubleExpression de) {  }
    public void Visit(SubtractionExpression ae) {  }
    public void Visit(Expression4 obj)
    {
        // domyślne zachowanie if i else
    }
    public override string ToString() =&amp;gt; sb.ToString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasza drukarka musi implementować "IVisitor" i wszystkie generyczne pod typy interfejsu "IVisitor&amp;lt;T&amp;gt;" dla każdego wyrażenia, kt&amp;oacute;re istnieje systemu.&lt;/p&gt;
&lt;p&gt;Niestety i tutaj pojawia się problem, z tym że nasze złożone wyrażenia zawierają po prostu definicję wyrażenia og&amp;oacute;lnego "Expression4".&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ExpressionPrinter4 : IVisitor,
IVisitor&amp;lt;Expression4&amp;gt;,
IVisitor&amp;lt;DoubleExpression&amp;gt;,
IVisitor&amp;lt;SubtractionExpression&amp;gt;
{
    StringBuilder sb = new StringBuilder();
    public void Visit(Expression4 obj)
    {
        if (obj is DoubleExpression)
            Visit(obj as DoubleExpression);
        if (obj is SubtractionExpression)
            Visit(obj as SubtractionExpression);
    }

    public void Visit(DoubleExpression de) 
    {
        sb.Append(de.Value);
    }
    public void Visit(SubtractionExpression se) 
    {
        sb.Append("(");
        se.Left.Accept(this);
        sb.Append("-");
        se.Right.Accept(this);
        sb.Append(")");
    }
    public override string ToString() =&amp;gt; sb.ToString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Znowu albo napiszemy if i else, aby obsłużyć taki problem ,albo skorzystam ze słowa kluczowego dynamic kosztem wydajności.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public void Visit(Expression4 obj)
{
    Visit((dynamic)obj);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sprawdźmy, czy podejście acykliczne działa pisząc taki kod:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var expression4 = new SubtractionExpression(
    left: new DoubleExpression(1),
    right: new SubtractionExpression(
    left: new DoubleExpression(2),
    right: new DoubleExpression(3)));

ExpressionPrinter4 expressionPrinter4
    = new ExpressionPrinter4();

expressionPrinter4.Visit(expression4);
Console.WriteLine(expressionPrinter4);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pamiętaj też, że w metodzie og&amp;oacute;lnej Visit() w naszej drukarce możemy także wyrzucać wyjątki, gdy trafi się metoda Visit, kt&amp;oacute;rą nie obsługujemy.&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;Wzorzec projektowy Visitor pozwala ci dodawać unikatowe zachowania dla każdego elementu danego wyrażenia, kt&amp;oacute;re też znajduje się w jakieś hierarchii.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Oto na ile sposob&amp;oacute;w możesz skorzystać z tego wzorca :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intrusive : Dodajemy metodę do każdego elementu określającego cegiełki naszego wyrażenia. P&amp;oacute;źniej zgodnie z hierarchią wykonasz zbi&amp;oacute;r operacji na tych cegiełkach. Te podejście łamie zasadę otwarte-zamknięte oraz zasadę pojedynczej odpowiedzialności.&lt;/li&gt;
&lt;li&gt;Reflective : Dodajemy oddzielną klasę w tym przypadku klasę operacji drukowania tak, aby nie robić zmian w naszych element. Takich oddzielnych klas możesz mieć wiele. Przykładowo stworzyliśmy inną klasę, kt&amp;oacute;ra wykonywała operacje matematyczne na naszym wyrażeniu.&lt;/li&gt;
&lt;li&gt;Dynamic : Zamiast pisać if i else możesz rzutować dany pod element na typ dynamiczny. To wszystko się stanie kosztem szybkości działania programu.&lt;/li&gt;
&lt;li&gt;Classic : Cała hierarchia element&amp;oacute;w ulega zmianie tylko raz.&amp;nbsp; Każdy element ma metodę Accept(), kt&amp;oacute;ra przyjmuje danego odwiedzającego.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Acyclic : Daje nam możliwość bardziej elastycznej relacji pomiędzy odwiedzającym a odwiedzanym.&amp;nbsp; Dzięki temu możemy mieć zbi&amp;oacute;r odwiedzających względem jednego zadania.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wzorzec projektowy Visitor łączy się ze wzorcem Interpreter. Dlatego ta dwa wzorce zostawiłem sobie na koniec tego cyklu.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Warto poznać podstawy tego wzorca, zanim skoczysz do wzorca Visitor stworzonych dla kompilatora Roslyn, kt&amp;oacute;ry potrafi analizować składnie napisanego kodu, jak i ją modyfikować.&lt;/p&gt;
&lt;p&gt;Ja mogę sobie gratulować, ponieważ w tym cyklu właśnie om&amp;oacute;wiliśmy wszystkie wzorce projektowe z Gang of Four. Pozostało mi przygotować jedno wielkie repozytorium GitHub z tymi wszystkim przykładami.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <category>wzorce-projektowe</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/visitor-odwiedzajacy-wzorce-projektowe-c</guid>
  <pubDate>Thu, 17 Feb 2022 13:11:09 GMT</pubDate>
</item>
<item>
  <title>Interpreter, Interpretator : Wzorce projektowe C#</title>
  <link>https://cezarywalenciuk.pl/blog/programing/interpreter-interpretator-wzorce-projektowe-c</link>
  <description>&lt;p&gt;[metro] Celem wzorca "Interpreter" jest zinterpretować dane wyjściowe zazwyczaj w formacie tekstowym, tak abyśmy mogli wykonać specyficzne akcje. Jednakże dane wyjściowymi nie muszą być koniecznie w formacie tekstowym.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"Interpreter" jest powiązany z kompilatorem. Warto zaznaczyć, że oba pojęcia nie m&amp;oacute;wią dokładnie o tym samym, chociaż można ich używać zamiennie.&amp;nbsp; Przypadku język&amp;oacute;w programowania r&amp;oacute;żnice są takie: [more]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kompilator : kompiluje język programowania do jakiegoś kodu maszynowego. Tak działają języki bazujące na C i C++ jak Java czy C#. W kompilatorze więc zawiera się Interpreter, bo w końcu coś musi zrozumieć składnie kodu, aby go przetransformować.&lt;/li&gt;
&lt;li&gt;Interpreter : nie przekształca kodu, tylko go uruchamia interpretując go. Tak działa Perl,Python, Matlab&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Być może na studiach zostałeś zmuszony napisać prymitywny kompilator, aby zaliczyć przedmiot.&amp;nbsp; Tak pisanie kodu w innym języku programowania, aby potem dostać kod maszynowy w wyniku kompilacji, jest też formą wzorca "Interpreter".&lt;/p&gt;
&lt;p&gt;"Interpreter" też może się ukrywać pod inną nazwą jak:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Parsers" tłumaczonych potocznie na polski parsery.&lt;/li&gt;
&lt;li&gt;oraz Analizatory składniowe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jakie przykładowe parsery mamy w .NET i og&amp;oacute;lnie w programowaniu:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wyrażenia regularne (Regular expressions)&lt;/strong&gt; : Co robimy, aby znaleźć specyficzne fragmenty tekstu ze skomplikowanymi zasadami.Przykładowo jakbyś napisał kod, kt&amp;oacute;ry by wyszukał wszystkie wystąpienia adres&amp;oacute;w e-mail w tekście. Na pomoc przychodzą wyrażenia regularne, kt&amp;oacute;re mają swoja składnie i specyficzne zasady. Każde wyrażenie jak na przykład te...&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs  "&gt;(?:[a-z0-9!#$%&amp;amp;'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&amp;amp;'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...potrzebuje swojego interpretera.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Struktury danych jak CSV, XML, JSON, YAML, pliki Excel&lt;/strong&gt; wymagają odpowiedniego interpretera, aby mogły być odczytane i użyte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wyrażenia drzewiaste&lt;/strong&gt; są kompilowane na wyrażenia lambda, czyli mają swojego interpretera.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Każdy język programowania ma sw&amp;oacute;j &lt;strong&gt;kompilator&lt;/strong&gt;, kt&amp;oacute;ry interpretuje kod i go transmituje na coś innego. Przykładowo TypeScript może być przetransformowany na język JavaScript poprzez interpretacje.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Na potrzeby tego wpisu. Stw&amp;oacute;rzmy więc sw&amp;oacute;j własny interpretator. Będziemy analizować tekst i zamieniać go na tokeny. Ta część programu nazywa się "Lexer".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;P&amp;oacute;źniej te tokeny zamienimy na poszczeg&amp;oacute;lne wartości, czyli będziemy "Parsować".&lt;/p&gt;
&lt;h3&gt;Co budujemy?&lt;/h3&gt;
&lt;p&gt;Naszym celem jest napisać program, kt&amp;oacute;ry przetłumaczy tekst pisany w stylu "&lt;strong&gt;/u/l/{tekst&lt;/strong&gt;" na wyrażenie HTML.&lt;/p&gt;
&lt;p&gt;Oto dwa przykłady tego, jak ma m&amp;oacute;j program ma działać.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/interpreter-interpretator--wzorce-projektowe-c/inter-01_637806121764367596.PNG" alt="Działanie naszego programu Interpreter" width="272" height="228" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/interpreter-interpretator--wzorce-projektowe-c/inter-02_637806121764491581.PNG" alt="Działanie naszego programu Interpreter" width="304" height="224" /&gt;&lt;/p&gt;
&lt;p&gt;Przykład nie jest idealny, ale jeśli będziesz budował sw&amp;oacute;j interpreter oto cegiełki do niego.&lt;/p&gt;
&lt;h3&gt;Lexing&lt;/h3&gt;
&lt;p&gt;Pierwsza operacja, kt&amp;oacute;ra nas interesuje będzie polegać na zmianie polecenia tekstowego na zbi&amp;oacute;r token&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Z czego powinien składać się nasz token. Powinien mieć informację o swoim typie oraz o przetrzymywanej wartości, kt&amp;oacute;ra będzie oznacza r&amp;oacute;żne rzeczy w zależności od kontekstu czy swojego typu.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Token
{
    public enum Type
    {
        Ul, Li, Text, 
    }

    public Type MyType;
    public string Text;
    public Token(Type type, string text)
    {
        MyType = type;
        Text = text;
    }
    public override string ToString()
    {
        return $"`{Text}`";
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz musimy mieć metodę, kt&amp;oacute;ra przetłumaczy te wyrażenie tekstowe na zbi&amp;oacute;r token&amp;oacute;w&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;IReadOnlyList&amp;lt;Token&amp;gt; Lex(string input)
{
    string[] words = input.Split("/");
    var result = new List&amp;lt;Token&amp;gt;();

    foreach (var word in words)
    {
        string text = "";
        if (word.Length &amp;gt; 1)
            text = word.Substring(1);
        if (word.Length == 0)
            continue;

        Token t;
        if (word[0] == 'u')
             t = new(Token.Type.Ul, text);
        else if(word[0] == 'l')
            t = new(Token.Type.Li, text);
        else if(word[0] == '{')
            t = new(Token.Type.Text, text);
        else
            t = new(Token.Type.Text, "");

        result.Add(t);
    }

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Logika jest następująca. Jeśli pierwszy znak to 'u' to wtedy uznaje, że chce wstawić element html &amp;lt;ul&amp;gt; gdzie dalszy tekst jest określeniem jego klasy CSS.&lt;/p&gt;
&lt;p&gt;Analogicznie postępuje dla znaku 'l', kt&amp;oacute;ry określa mi, że chce wstawić element html &amp;lt;li&amp;gt;.&lt;/p&gt;
&lt;p&gt;Natomiast nawias klamrowy '{' m&amp;oacute;wi, że teraz mam wstawić token tekstowy. M&amp;oacute;głbym umieścić więcej typ&amp;oacute;w token&amp;oacute;w, ale my chcemy prosty przykład, kt&amp;oacute;ry pomoże ci w przyszłości napisać podobny kod.&lt;/p&gt;
&lt;h3&gt;Parsowanie&lt;/h3&gt;
&lt;p&gt;Parsowanie będzie polegało na transformacji uniwersalnych token&amp;oacute;w na dokładniejsze klasy, kt&amp;oacute;re określą nam potem ostateczny wynik tekstowy danego wyrażenia.&lt;/p&gt;
&lt;p&gt;Pisząc taki interfejs łamię zasadę "Interface Segregation Principle".&lt;/p&gt;
&lt;p&gt;Nie każda właściwość będzie używana przez klasy implementujące ten interfejs , ale tak jak m&amp;oacute;wiłem ten przykład nie jest idealny.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IElement
{
    string Value { get; }

    string AfterValue { get; }

    public IElement Child { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz stw&amp;oacute;rzmy klasy, kt&amp;oacute;re będą określać zachowanie moich poszczeg&amp;oacute;lnych element&amp;oacute;w wyrażenia.&lt;/p&gt;
&lt;p&gt;Tak wygląda element tekstowy:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class TextElement : IElement
{
    public TextElement(string value)
    {
        Value = "    "+ value;
    }
    public string Value { get; }

    public IElement Child { get; set; }

    public string AfterValue =&amp;gt; "";
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak będzie wyglądać element &amp;lt;li&amp;gt; i &amp;lt;ul&amp;gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class UlElement : IElement
{
    public UlElement(string cssclass)
    {
        if (!string.IsNullOrWhiteSpace(cssclass))
            Value = $"&amp;lt;ul class=\"{cssclass}\"&amp;gt;";
        else
            Value = $"&amp;lt;ul&amp;gt;";

        AfterValue = "&amp;lt;/ul&amp;gt;";
    }
    public string Value { get; }


    public IElement Child { get; set; }

    public string AfterValue { get; }
}

public class LiElement : IElement
{
    public LiElement(string cssclass)
    {
        if (!string.IsNullOrWhiteSpace(cssclass))
            Value = $"&amp;lt;li class=\"{cssclass}\"&amp;gt;";
        else
            Value = $"&amp;lt;li&amp;gt;";

        AfterValue = "&amp;lt;/li&amp;gt;";
    }
    public string Value { get; }

    public IElement Child { get; set; }

    public string AfterValue { get; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Potrzebuje też klasy rodzica, kt&amp;oacute;ry będzie mi przetrzymywać te wszystkie elementy, a potem całe te wyrażenia przetworzy na ostateczny tekst.&lt;/p&gt;
&lt;p&gt;Ponieważ mamy element w elemencie to nie obędzie się bez rekurencji.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class HtmlOperation : IElement
{
    public IElement Child { get; set; }
    public string Value
    {
        get
        {
            StringBuilder sb = new StringBuilder();

            sb = Rec(sb,Child);

            return sb.ToString();
        }
    }

    public string AfterValue =&amp;gt; "";

    private StringBuilder Rec(StringBuilder sb, IElement e)
    {
        sb.AppendLine(e.Value);

        if (e.Child != null)
            Rec(sb, e.Child);

        if (e.AfterValue != null &amp;amp;&amp;amp; e.AfterValue != "")
            sb.AppendLine(e.AfterValue);

        return sb;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz gdy mamy wszystkie elementy układanki to pozostało nam to napisać metodę, kt&amp;oacute;ra zamieni te tokeny na nasze konkretne cegiełki HTML.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;static HtmlOperation Parse(IReadOnlyList&amp;lt;Token&amp;gt; tokens)
{
    HtmlOperation root = new HtmlOperation(); 
    bool haveLHS = false;
    for (int i = 0; i &amp;lt; tokens.Count; i++)
    {
        var token = tokens[i];
        // look at the type of token
        switch (token.MyType)
        {
            case Token.Type.Text:
                Insert(root,new TextElement(token.Text));
                break;
            case Token.Type.Ul:
                Insert(root, new UlElement(token.Text));
                break;
            case Token.Type.Li:
                Insert(root, new LiElement(token.Text));
                break;
        }
    }
    return root;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do tworzenia mojej struktury znowu będzie potrzebna rekurencja.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;static IElement Insert(IElement root, IElement nextchild)
{
    if (root.Child != null)
        return Insert(root.Child, nextchild);
    else
    {
        root.Child = nextchild;
        return root;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Program ten nie jest idealny. Po pierwsze nie łapie wszystkim możliwych nieprawidłowych wartości. Czy ten przykład można rozwijać? Oczywiście, że tak można do niego dodać kolejne tagi HTML i inne skr&amp;oacute;towe oznaczenia, kt&amp;oacute;re wydrukują się specyficznie w tekście końcowym.&lt;/p&gt;
&lt;h3&gt;Użycie naszego Parsera i Lexera.&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;Oto użycie naszego Parsera i Lexera. Program działać jak na obrazkach powyżej.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var input = "/ucssklaska/ltotezklaska/{tekst";

var tokens = Lex(input);

var parsed = Parse(tokens);

Console.WriteLine($"Ten zapis :\n");
Console.WriteLine($"{input} = \n");
Console.WriteLine($"Daje to :\n");
Console.WriteLine($"{parsed.Value}");&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;Wzorzec projektowy "Interpreter" jest bardzo rzadki. Pisanie swoich parser&amp;oacute;w zazwyczaj dotyczy jakiś błahych przykład&amp;oacute;w więc nie trzeba pisać tylu klas, aby wykonać swoje zadanie.&lt;/p&gt;
&lt;p&gt;Nawet ten prosty przykład wzorca można było napisać po prostu dużą grupą warunk&amp;oacute;w if i else. Chociaż z drugiej strony, jeśli planuje swojego Interpretera rozwijać to rozbicie tego kodu na tokeny i elementy do parsowania ma moim zdaniem sens.&lt;/p&gt;
&lt;p&gt;Jeśli interesuje Cię tematyka pisania własnych kompilator&amp;oacute;w to trochę źle trafiłeś. Mogę Cię odesłać do framework&amp;oacute;w jak&lt;strong&gt; "Lex/Yacc" i "ANTLR".&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Swoją przygodę też możesz zacząć od pisania plugin&amp;oacute;w do analizy kodu w samym Visual Studio.&lt;/p&gt;
&lt;p&gt;Kompilator Roslyn to też temat rzeka, kt&amp;oacute;ry nawet mnie kiedyś wciągnął do generowania dynamicznego całych projekt&amp;oacute;w z kodem w Visual Studio.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <category>wzorce-projektowe</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/interpreter-interpretator-wzorce-projektowe-c</guid>
  <pubDate>Wed, 16 Feb 2022 10:34:05 GMT</pubDate>
</item>
<item>
  <title>Proxy : Wzorzec projektowy C#</title>
  <link>https://cezarywalenciuk.pl/blog/programing/proxy-wzorzec-projektowy-c</link>
  <description>&lt;p&gt;[metro] We wzorcu projektowy "Dekorator" widzieliśmy jak można dodawać kolejne funkcjonalności bez zmiany oryginalnego zachowania. Wzorzec Proxy pr&amp;oacute;buje osiągnąć to samo tylko gorzej. Warto zaznaczyć, że ten wzorzec nie ma jednej słusznej implementacji. Wiele os&amp;oacute;b podchodzi do tego wzorca na wiele sposob&amp;oacute;w.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Gdyby pada słowo "Proxy" to zazwyczaj m&amp;oacute;wimy o pośredniku komunikacyjnym między serwerami, kt&amp;oacute;re gadają do siebie.&lt;/p&gt;
&lt;p&gt;Wzorzec projektowy Proxy też jest takim pośrednikiem między obiektami i jego rola polega na &lt;strong&gt;zabezpieczeniu,rozszerzeniu, zmodyfikowaniu&lt;/strong&gt; jakieś innej funkcji systemu, kt&amp;oacute;ra jest&amp;nbsp; pod nim.&lt;/p&gt;
&lt;p&gt;W zależności od celu wzorzec ten będzie miał inną implementację. Dlatego nie ma on jednego dobrego podejścia.&lt;/p&gt;
&lt;p&gt;Sp&amp;oacute;jrz więc na te r&amp;oacute;żne podejścia do tego wzorca. [more]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protection Proxy,&lt;/li&gt;
&lt;li&gt;Property Proxy,&lt;/li&gt;
&lt;li&gt;Virtual Proxy&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Communication Proxy&lt;/li&gt;
&lt;li&gt;i wiele innych, kt&amp;oacute;re możesz znaleźć w sieci&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Protection Proxy&lt;/h3&gt;
&lt;p&gt;Idea "Protection proxy" jak sama nazwa wskazuje polega na zabezpieczeniu pewnego obiektu przed jakimś warunkiem biznesowym.&lt;/p&gt;
&lt;p&gt;Mam więc komputer, kt&amp;oacute;ry potrafi uruchamiać kod.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Computer : IComputer 
{
    public void RunCode()
    {
        Console.WriteLine("WriteCode");
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;P&amp;oacute;źniej stwierdziłem, że nie każdy programista przed komputerem będzie m&amp;oacute;gł sw&amp;oacute;j kod uruchomić. Wymyśliłem taką zasadę biznesową, że jeśli jesteś programistą XML lub HTML to niestety komputer powinien odm&amp;oacute;wić uruchomienia twojego kodu.&lt;/p&gt;
&lt;p&gt;Z jakiegoś powodu nie mogę zmienić już swojej klasy Computer, aby to zrobić. Przyzwyczaja się do tego, bo zazwyczaj przez to, że nie możemy czegoś zmienić tworzymy klasy Proxy w każdym przykładzie w tym wpisie.&lt;/p&gt;
&lt;p&gt;Postanowiłem napisać interfejs, kt&amp;oacute;ry w żaden spos&amp;oacute;b nie modyfikuje już istniejącej klasy Computer.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public interface IComputer
{
    void RunCode();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasze proxy będzie zależne od "programisty". Ma on informację o tym, w czym on programuje.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Programer
{
    public PrograminLanguage Language { get; set; }
    public Programer(PrograminLanguage programinLanguage)
    {
        Language = programinLanguage;
    }
}

public enum PrograminLanguage
{
    JavaScript,
    CSharp,
    Java,
    HTML,
    XML
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jak wygląda nasze proxy? Jak widzisz pobieram sobie w konstruktorze instancje danego programisty.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class ComputerProxy : IComputer
{
    private Computer computer = new Computer();
    private Programer _programer;

    public ComputerProxy(Programer programer)
    {
        _programer = programer;
    }

    public void RunCode()&lt;br /&gt;    {
        if (_programer.Language == PrograminLanguage.HTML)
        {
            Console.WriteLine("Html nie jest językiem programowania więc tego nie uruchamiam");
            return;
        }
        if (_programer.Language == PrograminLanguage.XML)
        {
            Console.WriteLine("XML nie jest językiem programowania więc tego nie uruchamiam");
            return;
        }

        computer.RunCode();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;P&amp;oacute;źniej w zależności od tego, w czym nasz programista programuje to albo uruchomimy kod, albo wyświetliły wiadomość o tym właśnie naszym zabezpieczeniu przed programistami XML i HTML.&lt;/p&gt;
&lt;p&gt;Oto jak wygląda użycie naszego proxy.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;IComputer com = new ComputerProxy
    (new Programer(PrograminLanguage.HTML));
com.ExecuteCode();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Czy ten wzorzec jest dobry? Wiesz, że lubię zadawać takie pytania. Największy problem tego wzorca leży w tym, że o ile może się wydawać, że klasy Computer i ComputerProxy są podobne to mają zupełnie inne konstruktory.&lt;/p&gt;
&lt;p&gt;Czyli jeśli ComputerProxy ma służy za rozszerzenie istniejącej już funkcjonalności to ciężko tobię będzie zastąpić istniejące już użycia klasy Computer w systemie.&lt;/p&gt;
&lt;p&gt;Zwłaszcza jeśli nie korzystałeś ze wstrzykiwania zależności i musi teraz taką poprawę zrobić w całym systemie.&lt;/p&gt;
&lt;p&gt;Ten wzorzec ma sens, jeśli masz kontener wstrzykiwania zależności w przeciwnym wypadku niczego tak naprawdę nie rozwiązujesz i tak naprawdę tworzysz sobie kolejny problem na przyszłość.&lt;/p&gt;
&lt;h3&gt;Property Proxy&lt;/h3&gt;
&lt;p&gt;W C# korzysta z właściwości, kt&amp;oacute;re tak naprawdę są dobrze opakowanymi metodami GET i SET dla danego pola.&lt;/p&gt;
&lt;p&gt;Czasami chcesz, aby twoje właściwości robiły coś jeszcze, gdy następuje proces przypisania wartość lub jej pobierania.&lt;/p&gt;
&lt;p&gt;Dla tego przykładu chcemy zablokować przypisanie wartości do naszej właściwości, gdy pr&amp;oacute;bujemy ustawić jej wartość na domyślną albo na null.&lt;/p&gt;
&lt;p&gt;Korzystając z tego faktu możemy także w konsoli wyświetlić jakie wartości są w og&amp;oacute;le przypisywane do naszej właściwości.&lt;/p&gt;
&lt;p&gt;Moglibyśmy skorzystać z normalnych właściwości, aby osiągnąć ten cel, ale my chcemy zobaczyć użycie wzorca Proxy, kt&amp;oacute;ry nazywa się Property Proxy.&lt;/p&gt;
&lt;p&gt;Property Proxy opakuje nam każdą właściwości, abyśmy od razu mieli takie zachowanie. Potem będziemy mogli skorzystać z naszej klasy "Property Proxy" w wielu miejscach.&lt;/p&gt;
&lt;p&gt;To oczywiście odbije się kosztem wydajności, ale zał&amp;oacute;żmy, że nagle musimy do 1000 właściwości w r&amp;oacute;żnych klasach takie zachowanie dopisać i musisz tę zmianę w kodzie jakoś zautomatyzować do dalszych poprawek.&lt;/p&gt;
&lt;p&gt;Oto jak wygląda nasza klasa, kt&amp;oacute;ra stworzy nam "Property Proxy".&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Property&amp;lt;T&amp;gt; where T : new()
{
    private T value;
    private readonly string name;
    public T Value
    {
        get =&amp;gt; value;
        set
        {
            if (Equals(this.value, value)) return;
            if (value == null || value.Equals(default(T))) return;

            Console.WriteLine($"Assigning {value} to {name}");
            this.value = value;
        }
    }
    public Property() : this(default(T)) { }
    public Property(T value, string name = "")
    {
        this.value = value;
        this.name = name;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Zwr&amp;oacute;ć uwagę na blokadę uzupełniania wartości w tej klasie.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;set
{
    if (Equals(this.value, value)) return;
    if (value == null || value.Equals(default(T))) return;

    Console.WriteLine($"Assigning {value} to {name}");
    this.value = value;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;Musi on zawierać jawną konwersję na typ, kt&amp;oacute;ry będziemy opakowywać.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public static implicit operator T(Property&amp;lt;T&amp;gt; property)
{
    return property.Value; 
}
public static implicit operator Property&amp;lt;T&amp;gt;(T value)
{
    return new Property&amp;lt;T&amp;gt;(value); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pozostało nam już użyć tego wzorca:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Programer
{
    public Property&amp;lt;int&amp;gt; Luck
      = new Property&amp;lt;int&amp;gt;(10, nameof(Luck));

    public Property&amp;lt;int&amp;gt; Wisdom
        = new Property&amp;lt;int&amp;gt;(6, nameof(Wisdom));

    public Property&amp;lt;int&amp;gt; Intelligence
        = new Property&amp;lt;int&amp;gt;(7, nameof(Intelligence));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Niestety takie podejście nie działa. Ten kod nie zrobi nic.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var p = new Programer();
p.Wisdom = 12;
p.Luck = 12;
p.Intelligence = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Problem leży w jawnej konwersji naszego proxy, kt&amp;oacute;ra niestety nie potrafi modyfikować istniejącej właściwości. Taki kod zadziała, ale nie o to nam chodzi:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var p1 = new Programer();
p1.Wisdom.Value = 12;
p1.Luck.Value = 12;
p1.Intelligence.Value = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rozwiązanie polega na stworzeniu p&amp;oacute;l, kt&amp;oacute;re będą opakowane naszym proxy, a te pola będą referować się do istniejących już właściwości&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Programer2
{
    public readonly Property&amp;lt;int&amp;gt; luck
    = new Property&amp;lt;int&amp;gt;(10, nameof(luck));

    public readonly Property&amp;lt;int&amp;gt; wisdom = 
        new Property&amp;lt;int&amp;gt;(6, nameof(wisdom));

    public readonly Property&amp;lt;int&amp;gt; intelligence =
    new Property&amp;lt;int&amp;gt;(7, nameof(intelligence));

    public int Luck
    {
        get =&amp;gt; luck.Value;
        set =&amp;gt; luck.Value = value;
    }

    public int Wisdom
    {
        get =&amp;gt; wisdom.Value;
        set =&amp;gt; wisdom.Value = value;
    }

    public int Intelligence
    {
        get =&amp;gt; intelligence.Value;
        set =&amp;gt; intelligence.Value = value;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz ten kod zadziała.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var p2 = new Programer2();
p2.Wisdom = 12;
p2.Luck = 12;
p2.Intelligence = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ten wzorzec wygląda kiepsko, ponieważ trzeba stworzyć oddzielną klasę do tego wzorca, jak i zmodyfikować istniejący już kod, a tego właśnie chcieliśmy uniknąć używając wzorca Proxy.&lt;/p&gt;
&lt;p&gt;Te problemy mogą wynikać z ograniczeń języka C#, ale ciężko jest obronić ten wzorzec. Zmodyfikowaliśmy lekko kod, aby w teorii nie pisać jeszcze więcej kodu, kt&amp;oacute;ry będzie się powtarzać, ale to od Ciebie zależy decyzja czy ten wzorzec ma sens.&lt;/p&gt;
&lt;h3&gt;Virtual Proxy&lt;/h3&gt;
&lt;p&gt;Virtual Proxy już istnieje i nazywa się Lazy&amp;lt;T&amp;gt;.&lt;/p&gt;
&lt;p&gt;Lazy&amp;lt;T&amp;gt; utworzy Ci instancje obiektu, gdy ten będzie potrzebny. Chodzi o to, aby uniknąć czkawki przy pierwszym uruchomieniu całego systemu, gdy wszystkie obiekty się tworzą.&lt;/p&gt;
&lt;p&gt;Na potrzeby wpisu szybko utworzę swoje Wirtualne Proxy.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;interface IFile
{
    void Show();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rola "Virtual Proxy" polega na użyciu oryginalnego API tak, aby stworzyć iluzję jego instancji. A tak naprawdę to oryginalne API zostanie utworzone, dopiero gdy będzie ono potrzebne.&lt;/p&gt;
&lt;p&gt;Oto klasa, kt&amp;oacute;ra odczyta plik tekstowy. Jak widzisz operację odczytu pliku zrobi się już w konstruktorze.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;class TextFile : IFile
{
    private readonly string _path;

    private readonly List&amp;lt;string&amp;gt; _texts;

    private int _lines;
    public TextFile(string path)
    {
        this._path = path;
        _texts = new List&amp;lt;string&amp;gt;();

        using (StreamReader file = new StreamReader(_path))
        {
            int counter = 0;
            string ln;

            while ((ln = file.ReadLine()) != null)
            {
                _texts.Add(ln);
                counter++;
            }
            file.Close();
            _lines = counter;

        }
    }

    public void Show()
    {
        foreach (var item in _texts)
        {
            Console.WriteLine("===========");
            Console.WriteLine(item);
        }
        Console.WriteLine($"How many lines : {_lines}");
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chcielibyśmy jednak aby ten konstruktor nie pr&amp;oacute;bował otworzyć pliku natychmiast.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;TextFile file = new TextFile(@"D:\text.txt");&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Zał&amp;oacute;żmy, że nie możemy zmieniać tej istniejącej klasy, czyli musimy napisać swoje proxy, kt&amp;oacute;re nam zmodyfikuje takie zachowanie.&lt;/p&gt;
&lt;p&gt;Oto nasze "Virtual Proxy"&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;class LazyTextFile : IFile
{
    private readonly string _path;

    private TextFile _textFile;

    public LazyTextFile(string path)
    {
        this._path = path;
    }

    public void Show()
    {
        if (_textFile == null)
            _textFile = new TextFile(this._path);

        _textFile.Show();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ta klasa stworzy instancje "TextFile" gdy ten będzie potrzebny, gdy skorzystamy z metody "Show()".&lt;/p&gt;
&lt;p&gt;Obie klasy korzystają z tego samego interfejsu i mają taki sam konstruktor. Znaczy to, że łatwo możesz podmienić implementacje.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;LazyTextFile file = new LazyTextFile(@"D:\text.txt");

file.Show();&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Communication Proxy&lt;/h3&gt;
&lt;p&gt;Zadaniem tego proxy jest zasymulować zachowanie pewnego serwisu i jego metody, gdy ten znajduje się gdzieś w innym zasobie sieciowym.&lt;/p&gt;
&lt;p&gt;Oto prosty przykład takiego proxy. Mamy interfejs, kt&amp;oacute;ry sprawdza, czy dany serwis działa.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;interface ICheck
{
    string Check(string message);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W aplikacji ASP.NET Core te sprawdzenie wyglądałoby tak.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;[Route("api/[controller]")]
public class HomeController : ICheck
{
    [HttpGet("{message}")]
    public string Check(string message)
    {
        return message + "pong";
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W innej aplikacji chcemy uruchomić tę metodę. Tylko dla niej te API istnieje jako zas&amp;oacute;b sieciowy, kt&amp;oacute;ry żyje pod jakiś adresem.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;class RemoteHomeCheck : ICheck
{
    public string Check(string message)
    {
        string uri = "http://localhost:7149/api/home/" + message;
        return new WebClient().DownloadString(uri);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasze "Communication Proxy" ten problem rozwiąże i z punktu widzenia użytkownika tworzymy iluzję tego, że ta usługa jest w naszej aplikacji.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Chociaż tak naprawdę wysyłamy zapytanie HTTP, aby otrzymać rezultat oryginalnego API.&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;W tym wpisie pokazałem Ci wiele r&amp;oacute;żnych proxy. W przeciwieństwie do dekoratora Proxy nie wystawia publicznie oryginalnego API, kt&amp;oacute;re on przykrywa.&lt;/p&gt;
&lt;p&gt;Zazwyczaj nie wymaga on modyfikacji oryginalnego&amp;nbsp;kodu, ale jak sam się przekonałeś nie zawsze tak jest.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Analogicznie możemy napisać "Logging Proxy", kt&amp;oacute;ry jak się domyślasz przykrywa oryginalne API tylko po to, aby dodać informację o logowaniu.&lt;/p&gt;
&lt;p&gt;Istnieje mn&amp;oacute;stwo wzorc&amp;oacute;w "Proxy". Istnieje szansa, że sam napisałeś w jakimś celu Proxy i nawet o tym nie wiedziałeś, że jest to oficjalny wzorzec projektowy.&amp;nbsp;&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <category>wzorce-projektowe</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/proxy-wzorzec-projektowy-c</guid>
  <pubDate>Tue, 15 Feb 2022 08:57:13 GMT</pubDate>
</item>
<item>
  <title>Flyweight, Pyłek : Wzorce projektowe C#</title>
  <link>https://cezarywalenciuk.pl/blog/programing/flyweight-pylek--wzorce-projektowe-c</link>
  <description>&lt;p&gt;[metro]Flyweight czy Pyłek jest to tymczasowy komponent, kt&amp;oacute;ry jest sprytną referencją do czegoś większego.&lt;/p&gt;
&lt;p&gt;Ten wzorzec projektowy najczęściej jest używany, gdy masz dużą ilość podobnych do siebie obiekt&amp;oacute;w i chcesz ograniczyć ilość zajmowanej pamięci poprzez wydzielenie powtarzających się wartości do jednego obiektu, do kt&amp;oacute;rego wszystkie inne obiekty będą się referować.&lt;/p&gt;
&lt;p&gt;Sp&amp;oacute;jrz na poniższy przykład [more]&lt;/p&gt;
&lt;h3&gt;Miliard kontrakt&amp;oacute;w z wartościami, kt&amp;oacute;re będą się powtarzać&lt;/h3&gt;
&lt;p&gt;Wyobraź sobie, że mamy aplikację, w kt&amp;oacute;rej mamy milion użytkownik&amp;oacute;w i każdy z nich ma po 20 r&amp;oacute;żnych kontrakt&amp;oacute;w. Już na podstawie tej informacji wiemy, że pewne informacje będą się powtarzać.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Imiona i nazwiska jak "&lt;em&gt;Jan Kowalski&lt;/em&gt;" są bardzo popularne, więc i tu będą powt&amp;oacute;rzenia, nawet gdy m&amp;oacute;wimy o osobnych użytkownikach względem wszystkich kontrakt&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Ciągle też dochodzą inne typy um&amp;oacute;w więc ich wszystkich nigdy nie zdefiniowaliśmy, ale wiemy, że się powtarzają wśr&amp;oacute;d wszystkich użytkownik&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Co możemy z tym zrobić? Pomyśl w teorii obecnie istnieje nawet szansa powt&amp;oacute;rzenia kontraktu, dla kt&amp;oacute;rego klient ma imie i nazwisko "&lt;em&gt;Jan Kowalsk&lt;/em&gt;i" i ma typ um&amp;oacute;w "Q&lt;em&gt;WERT SMALL 1289&lt;/em&gt;".&lt;/p&gt;
&lt;p&gt;Oczywiście, żeby wyr&amp;oacute;żnić unikatowych kontrakt dla konkretnego klient, to powinniśmy mieć jakiś jeszcze unikatowy identyfikator jak PESEL.&lt;/p&gt;
&lt;p&gt;My jednak m&amp;oacute;wimy tutaj o problemie związany z tym, że te wszystkie wartości, kt&amp;oacute;re się powtarzają będą zajmować miejsce w pamięci.&lt;/p&gt;
&lt;p&gt;Tradycyjnie taką klasę byśmy napisali tak:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Contract
{
    public string CustomerFullName { get;  }

    public string TypeDeal { get; }

    public Contract(string fullname, string typedeal)
    {
        CustomerFullName = fullname;
        TypeDeal = typedeal;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Przy użyciu wzorca "FlyWeight" możemy taką klasę napisać inaczej.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class Contract2
{
    private static List&amp;lt;string&amp;gt; _cachedNames = new List&amp;lt;string&amp;gt;();
    private int[] flyweightsNames;

    private static List&amp;lt;string&amp;gt; _cachedTypeDeal = new List&amp;lt;string&amp;gt;();
    private int[] flyweightsTypeDeal;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Możemy wszystkie imiona i typy um&amp;oacute;w, kt&amp;oacute;re będą latać w naszej aplikacji przechowywać w statycznej liście. Co więcej, my chcemy zachować poszczeg&amp;oacute;lne wyraz w imionach i nazwiskach oraz typach um&amp;oacute;w i szukać tam duplikat&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Znaczy to, że będziemy mogli przechowywać w jednym miejscu w pamięci wszystkich Jan&amp;oacute;w i Dark&amp;oacute;w niezależnie od tego, jakie mają nazwisko.&lt;/p&gt;
&lt;p&gt;To samo tyczy się typ&amp;oacute;w um&amp;oacute;w. Dla typu kontraktu "QWERT SMALL 1289" i "HALO SMALL" będziemy mogli wsp&amp;oacute;łdzielić wyraz "SMALL".&lt;/p&gt;
&lt;p&gt;Czym jest "&lt;strong&gt;Flyweight&lt;/strong&gt;" w tym kontekście?&lt;/p&gt;
&lt;p&gt;Jest nim tablica indeks&amp;oacute;w, kt&amp;oacute;ra będzie referować się do specyficznych wartości w tych statycznych listach. Tablica indeks&amp;oacute;w typu Int32 będzie zajmować dużo mniej pamięci niż całe wyrazy wewnątrz naszego konkretnego kontraktu.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public Contract2(string fullname, string typedeal)
{
    int getOrAdd(string s, List&amp;lt;string&amp;gt; cache)
    {
        int idx = cache.IndexOf(s);
        if (idx != -1) return idx;
        else
        {
            cache.Add(s);
            return cache.Count - 1;
        }
    }

    flyweightsNames = fullname.Split(' ')
        .Select(s =&amp;gt; getOrAdd(s, _cachedNames)).ToArray();

    flyweightsTypeDeal = typedeal.Split(' ')
        .Select(s =&amp;gt; getOrAdd(s, _cachedTypeDeal)).ToArray();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;W trakcie tworzenia naszego kontraktu będziemy umieszczać wydzielone imiona i nazwiska do naszej listy statycznej.&lt;/p&gt;
&lt;p&gt;Nasze poprzednie właściwości będą teraz wyciągać imiona i nazwiska oraz typy um&amp;oacute;w z tych statycznych list.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public string CustomerFullName =&amp;gt; string.Join(" ",
    flyweightsNames.Select(i =&amp;gt;_cachedNames[i]));

public string TypeDeal =&amp;gt; string.Join(" ",
flyweightsTypeDeal.Select(i =&amp;gt; _cachedTypeDeal[i]));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wszystko. Rodzi się oczywiście pytanie, czy to rzeczywiście zajmuje mniej pamięci.&lt;/p&gt;
&lt;p&gt;Pr&amp;oacute;bowałem paru metod mierzenia pamięci, ale ostatecznie musiałem skorzystać z płatnego narzędzia dotMemory w wersji Trial.&lt;/p&gt;
&lt;p&gt;Napisałem taki kod, aby sprawdzić, kt&amp;oacute;ra z wersji będzie zajmować mniej pamięci.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;List&amp;lt;Contract&amp;gt; list;
{
    List&amp;lt;string&amp;gt; firstNames = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; surNames = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; typeDealPart1 = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; typeDealPart2 = new List&amp;lt;string&amp;gt;();

    list = new List&amp;lt;Contract&amp;gt;();

    for (int i = 0; i &amp;lt; 10; i++)
    {
        var a1 = RandomString();
        var a2 = RandomString();
        var a3 = RandomString();
        var a4 = RandomString();

        for (int j = 0; j &amp;lt; 10; i++)
        {
            list.Add(new Contract
                ($"{a1} {a2}"
                , $"{a3} {a4}"));
        }
        Console.WriteLine(i);
    }

    Console.WriteLine();
}
Console.WriteLine("Done");&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Każdy napis będzie składać się z 10 losowych znak&amp;oacute;w. Dla imion i nazwisk oraz typ&amp;oacute;w kontrakt&amp;oacute;w będziemy mieć przynajmniej 10 powt&amp;oacute;rzeń.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;string RandomString()
{
    Random rand = new Random();
    return new string(
    Enumerable.Range(0, 10)
    .Select(i =&amp;gt; (char)('a' + rand.
    Next(26))).ToArray());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pozostało uruchomić ten program w wersji Trial i zobaczyć, jakie będą wyniki.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/rehsarper-01_637805093200796711.png" alt="Użycie dotmemory w Visual Studio 2022" width="750" height="211" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/dotmemor-01_637805093200922895.PNG" alt="DotMemory" width="508" height="185" /&gt;&lt;/p&gt;
&lt;p&gt;Parametry testu są takie : mamy p&amp;oacute;ł miliona kontrakt&amp;oacute;w i mamy 10 powt&amp;oacute;rzeń wartości.Czyli co 50.000 kontrakt ma podobne parametry.&lt;/p&gt;
&lt;p&gt;Dla tradycyjnego kontraktu wynik wygląda tak.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/dot-01_637805093200953023.PNG" alt="Wyniki dla Contract bez wzorca" width="489" height="51" /&gt;&lt;/p&gt;
&lt;p&gt;Natomiast wynik dla wzorca FlyWeight wygląda tak.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/a-05_637805093200975186.PNG" alt="Wyniki dla Contract z wzorcem FlyWeight" width="578" height="74" /&gt;&lt;/p&gt;
&lt;p&gt;Jak widać nas wzorzec dla takich parametr&amp;oacute;w zajął tyle samo pamięci.&lt;/p&gt;
&lt;p&gt;Ten niby zysk pamięci wynikający z ilość powt&amp;oacute;rzeń jakoś został odjęty od ilość pamięci, kt&amp;oacute;ra jest potrzebna do stworzenia tablicy liczb całkowitych, kt&amp;oacute;re referują się do przetrzymywanych powtarzających się wartości.&lt;/p&gt;
&lt;p&gt;Co trzeba zrobić , aby zobaczyć pozytywny rezultat tego wzorca ? Trzeba zrobić więcej potworzeń wartości.&lt;/p&gt;
&lt;p&gt;Warto zaznaczyć, że kod ze wzorcem FlyWeight jest dużo wolniejszy od tradycyjnego podejścia. Śmiałoby powiedział, że ten kod jest 5-10 razy wolniejszy od tradycyjnego podejścia, chociaż nie mierzyłem tego ze stoperem.&lt;/p&gt;
&lt;p&gt;Jak widać użycie tego wzorca musi być przemyślane.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;List&amp;lt;Contract2&amp;gt; list;
{
    List&amp;lt;string&amp;gt; firstNames = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; surNames = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; typeDealPart1 = new List&amp;lt;string&amp;gt;();

    List&amp;lt;string&amp;gt; typeDealPart2 = new List&amp;lt;string&amp;gt;();

    list = new List&amp;lt;Contract2&amp;gt;();

    for (int i = 0; i &amp;lt; 50; i++)
    {
        var a1 = RandomString();
        var a2 = RandomString();
        var a3 = RandomString();
        var a4 = RandomString();

        for (int j = 0; j &amp;lt; 100; j++)
        {
            list.Add(new Contract2
                ($"{a1} {a2}"
                , $"{a3} {a4}"));
        }
        Console.WriteLine(i);
    }

    Console.WriteLine("Done");
    Console.Read();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Patrząc na poprzedni przykład możemy zauważyć, że wyniki w Megabajtach&amp;nbsp; nam dużo nie m&amp;oacute;wią dlatego zmniejszy rozmiar generowanej kolekcji, abyśmy mogli zobaczyć r&amp;oacute;żnice wynik&amp;oacute;w na podstawie poszczeg&amp;oacute;lnych bajt&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Poza tym testowanie p&amp;oacute;ł milionowej kolekcji dla wzorca FlyWeight trwało dobre 45 minut. Taka informacja dla Ciebie, gdybyś chciał testować podobny kod.&lt;/p&gt;
&lt;p&gt;Parametry testu są takie : mamy 5000 kontrakt&amp;oacute;w i mamy 100 powt&amp;oacute;rzeń wartości. Czyli co 50 kontrakt ma podobne parametry.&lt;/p&gt;
&lt;p&gt;Teraz taki wynik mamy dla kontraktu.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/a-04_637805093200992239.PNG" alt="Gorsze wyniki dla Contract bez wzorca" width="517" height="25" /&gt;&lt;/p&gt;
&lt;p&gt;A taki dla naszego wzorca FlyWeight&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pyłek--wzorce-projektowe-c/a-04_637805093201009111.PNG" alt="Lepsze wyniki dla Contract z wzorce FlyWeight" width="492" height="35" /&gt;&lt;/p&gt;
&lt;p&gt;Czego się więc nauczyliśmy? Faktu, że trzeba używać tego wzorca z głową, gdy masz pewność, że liczba powtarzających się wartości w całym systemie przekroczy przynajmniej liczbę 10.&lt;/p&gt;
&lt;p&gt;Dodatkowo wzorzec FlyWeight m&amp;oacute;głby zajmować jeszcze więcej pamięci niż tradycyjne podejście, jeślibyśmy nie mielibyśmy potworzeń.&lt;/p&gt;
&lt;p&gt;FlyWeight ma sens na pewno, gdy wiesz, że tw&amp;oacute;j wskaźnik zajmuje mniej pamięci niż cały obiekt, do kt&amp;oacute;rego on się referuje.&lt;/p&gt;
&lt;p&gt;Wzorzec FlyWeight też ma inne zastosowanie.&lt;/p&gt;
&lt;h3&gt;FlyWeight jako token modyfikujący&lt;span style="font-size: 14px;"&gt;&amp;nbsp;:&amp;nbsp;&lt;/span&gt;Przykład z formatowaniem tekstu&lt;/h3&gt;
&lt;p&gt;Wzorzec FlyWeight wymaga często głębokiej analizy, jakie wartości możemy ztokenizować tak, aby nie tworzyć kolejnych instancji pewnego obiektu, aby osiągnąć trochę inny cel przy niewielkiej modyfikacji parametr&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;Wyobraź sobie obiekt, kt&amp;oacute;ry przechowuje tekst, kt&amp;oacute;ry ma z milion znak&amp;oacute;w. W nim także mamy meta informację :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;O wielkości znak&amp;oacute;w w danym obszarze napisu. Przykładowo określamy, że znaki od 15 do 20 w napisie mają być pisane wielką literą.&lt;/li&gt;
&lt;li&gt;O kolorze napisu w danym obszarze tego napisu. Przykładowo określamy, że znaki od 15 do 20&amp;nbsp;w napisie&amp;nbsp;mają być pisane na czerwono.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;W czym jest problem? Sp&amp;oacute;jrz na tą klasę, kt&amp;oacute;ra reprezentuje taki obiekt.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class FormattedText
{
    private string plainText;

    public FormattedText(string plainText)
    {
        this.plainText = plainText;
        _capitalize = new bool[plainText.Length];
        _colorize = new bool[plainText.Length];
    }
    public void Capitalize(int start, int end)
    {
        for (int i = start; i &amp;lt;= end; ++i)
            _capitalize[i] = true;
    }

    public void Colorize(int start, int end)
    {
        for (int i = start; i &amp;lt;= end; ++i)
            _colorize[i] = true;
    }

    public void SetColor(string c)
    {
        _color = c;
    }

    private bool[] _capitalize;
    private bool[] _colorize;
    private string _color;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak będziemy generować napis.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public void Write()
{
    for (var i = 0; i &amp;lt; plainText.Length; i++)
    {
        var c = plainText[i];

        if (_capitalize[i])
        {
            c = char.ToUpper(c);
        }
        if (_colorize[i])
        {
            if (_color == "Red")
                Console.ForegroundColor = ConsoleColor.Red;
            if (_color == "Green")
                Console.ForegroundColor = ConsoleColor.Green;     
        }

        Console.Write(c);
        Console.ResetColor();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Teraz chce zmienić identyczny napis na dwa r&amp;oacute;żne sposoby. Niestety z tego powodu muszę stworzyć dwie instancje swojej klasy. Co oczywiście zajmuje więcej pamięci.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var ft = new FormattedText
    ("In the land of the blind, the one-eyed man is king\n");
ft.Capitalize(19, 24);
ft.Colorize(19, 24);
ft.SetColor("Red");
ft.Write();

var ft2 = new FormattedText
    ("In the land of the blind, the one-eyed man is king\n");
ft2.Colorize(19, 24);
ft2.SetColor("Green");
ft2.Write();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pylek--wzorce-projektowe-c/flyweight rsult_637813787173411500.PNG" alt="Obrazowanie problemu" width="410" height="47" /&gt;&lt;/p&gt;
&lt;p&gt;Co możemy z tym zrobić?&lt;/p&gt;
&lt;p&gt;Tablica znak&amp;oacute;w, czyli string wypadałoby wydzielić od informacji o tym, jak tekst powinien być w modyfikowany w danych obszarach.&lt;/p&gt;
&lt;p&gt;To dam możliwość wielokrotnej modyfikacji tego samego napisu bez tworzenia kolejny instancji podobnych obiekt&amp;oacute;w.&lt;/p&gt;
&lt;p&gt;FlyWeight daje nam moc takich token&amp;oacute;w, kt&amp;oacute;re modyfikują część mechanizmu bez tworzenia go całkowicie od nowa jeszcze raz.&lt;/p&gt;
&lt;p&gt;Oto nasz obiekt, kt&amp;oacute;ry da nam możliwość modyfikacji formatowania.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class TextRange
{
    public int Start, End;
    public bool Capitalize;
    public bool Colorize;
    public string Color;
    public bool ChooseRange(int position)
    {
        return position &amp;gt;= Start &amp;amp;&amp;amp; position &amp;lt;= End;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nasza klasa też się zmieniła. Mamy teraz możliwość dodania wielu takich modyfikacji obszarowych na raz dla jednego napisu.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public class BetterFormattedText
{
    private readonly string plainText;
    private readonly List&amp;lt;TextRange&amp;gt; formatting
    = new List&amp;lt;TextRange&amp;gt;();

    public BetterFormattedText(string plainText)
    {
        this.plainText = plainText;
    }
    public TextRange GetRange(int start, int end)
    {
        var range = new TextRange { Start = start, End = end };
        formatting.Add(range);
        return range;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tak teraz wygląda metoda Write().&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;public void Write()
{
    for (var i = 0; i &amp;lt; plainText.Length; i++)
    {
        var c = plainText[i];
        foreach (var range in formatting)
        {
            
            if (range.ChooseRange(i) &amp;amp;&amp;amp; range.Capitalize)
            {
                c = char.ToUpper(c);
            }
            if (range.ChooseRange(i) &amp;amp;&amp;amp; range.Colorize)
            {
                if (range.Color == "Red")
                    Console.ForegroundColor = ConsoleColor.Red;
                if (range.Color == "Green")
                    Console.ForegroundColor = ConsoleColor.Green;        
            }

            Console.Write(c);
            Console.ResetColor();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A tak wygląda użycie naszej klasy. Jak widzisz dla drugiej modyfikacji nie muszę tworzy od nowa całego obiektu, czyli tworzyć napisu dwa razy. Co jest istotne, gdybyśmy m&amp;oacute;wili o tekście, kt&amp;oacute;ry ma milion znak&amp;oacute;w.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs cs "&gt;var bft = new BetterFormattedText
    ("In the land of the blind, the one-eyed man is king\n");
var range = bft.GetRange(19, 24);
range.Color = "Green";
range.Capitalize = true;
range.Colorize = true;

bft.Write();
range.Color = "Red";
range.Capitalize = false;
range.Colorize = true;
bft.Write();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Przykład może nie jest idealny, ale chciałem Ci zobrazować jak tokenizacja pewnych mechanizm&amp;oacute;w może Ci oszczędzić miejsce w pamięci.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="/Posts/programing/files/2022/flyweight-pylek--wzorce-projektowe-c/flyweight rsult_637813787173494595.PNG" alt="FlyWeight jako token modyfikujący  Przykład z formatowaniem tekstu" width="403" height="35" /&gt;&lt;/p&gt;
&lt;h3&gt;Podsumowanie:&lt;/h3&gt;
&lt;p&gt;Flyweight jest to technika, kt&amp;oacute;rej celem jest oszczędność pamięci komputera. Czasem jego użycie jest jawne. Przykładowo aplikacja zwraca Ci token, kt&amp;oacute;ry p&amp;oacute;źniej pozwoli ci modyfikować cokolwiek jest do tego tokenu podpięte. Zrobiliśmy taki trik z naszym edytorem tekstowym.&lt;/p&gt;
&lt;p&gt;Gdy Flyweight jest niejawne to wtedy klient nawet nie wie, że taki wzorzec jest użyty. Ten trik zobaczyłeś, gdy tworzyliśmy Kontrakty.&lt;/p&gt;
&lt;p&gt;W frameworku .NET taki wzorzec już istnieje i jest nim typ generyczny Span&amp;lt;T&amp;gt;. Span&amp;lt;T&amp;gt; przechowuje w sobie informacje o punkcie początkowym, jak i długość pewnych tablic.&lt;/p&gt;
&lt;p&gt;Jest to bardzo zaawansowana technika, ale rzeczywiście, jeśli myślisz o optymalizacji swojego programu gdzie krążą napisy lub inne duże obiekty to warto poczytać o Span&amp;lt;T&amp;gt;.&lt;/p&gt;</description>
  <author>Cezary Walenciuk</author>
  <category>c#</category>
  <category>wzorce-projektowe</category>
  <guid isPermaLink="false">https://cezarywalenciuk.pl/blog/programing/flyweight-pylek--wzorce-projektowe-c</guid>
  <pubDate>Mon, 14 Feb 2022 11:55:53 GMT</pubDate>
</item></channel>
</rss>