O braciach QueryString i ActionParameters

W .net ASP.MVC są różne mechanizmy. Są też takie, które umożliwiają zbadanie argumentów przesłanych do akcji, jak i argumentów oczekiwanych w akcji. I właśnie o nich dzisiaj. Można je wykorzystywać na dobry i zły sposób, jak każde narzędzie. Najpierw mały pokaz a potem filozofowanie. Mamy taki kod:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:  public class HomeController : Controller
   2:  {
   3:      public ActionResult Index()
   4:      {
   5:          return this.View("Index");
   6:      }
   7:   
   8:      public string MustHaveString(string text)
   9:      {
  10:          return text;
  11:      }
  12:   
  13:      public string CanHaveString(string text = "")
  14:      {
  15:          return text;
  16:      }
  17:   
  18:      public string MustHaveInt(int input)
  19:      {
  20:          return input.ToString();
  21:      }
  22:   
  23:      public string CanHaveInt(int input = 0)
  24:      {
  25:          return input.ToString();
  26:      }
  27:   
  28:      public string MustHaveBoth(string text, int input)
  29:      {
  30:          string r = string.Empty;
  31:          for (int i = 0; i < input; i++)
  32:          {
  33:              r += text;
  34:          }
  35:          return r;
  36:      }
  37:   
  38:      public string CanHaveBoth(string text = "just a text", int input = 7)
  39:      {
  40:          string r = string.Empty;
  41:          for (int i = 0; i < input; i++)
  42:          {
  43:              r += text;
  44:          }
  45:          return r;
  46:      }
  47:   
  48:      public string Greed(string text, int input, int wantSoMuch, int stillNotUsing, int itAll)
  49:      {
  50:          return text + input;
  51:      }
  52:  }

Idąc od góry oczekujemy stringa, następnie oczekujemy stringa, ale mamy wartość domyślną na wypadek nie przesłania do przez użytkownika. Dalej to samo z intem, obowiązkowo oraz opcjonalnie. Potem wariacja z dwoma parametrami – obowiązkowo i opcjonalnie. I na koniec strasznie poznańska metoda, która potrzebuje wielu paramentrów, ale wykorzystuje tylko kilka z nich. Jeszcze jeszcze index, którego wyświetlenie wygląda tak:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:  <div class="container">
   2:      <div class="row">
   3:          <div class="col-md-6">@Html.ActionLink("Must have a string with string set", "MustHaveString", "Home", new { text = "lorem ipsum" }, new { @class = "btn btn-default" })</div>
   4:      </div>
   5:      <div class="row">
   6:          <div class="col-md-6">@Html.ActionLink("Must have a string with string not", "MustHaveString", "Home", new {}, new { @class = "btn btn-default" })</div>
   7:      </div>
   8:      <div class="row">
   9:          <div class="col-md-6">@Html.ActionLink("Can have a string with string set", "CanHaveString", "Home", new { }, new { @class = "btn btn-default" })</div>
  10:      </div>
  11:      <div class="row">
  12:          <div class="col-md-6">@Html.ActionLink("Can have a string with string not set", "CanHaveString", "Home", new {text="litwo ojczyzno moja" }, new { @class = "btn btn-default" })</div>
  13:      </div>
  14:      <hr/>
  15:      <div class="row">
  16:          <div class="col-md-6">@Html.ActionLink("Must have a int with int set", "MustHaveInt", "Home", new { input = 42 }, new { @class = "btn btn-default" })</div>
  17:      </div>
  18:      <div class="row">
  19:          <div class="col-md-6">@Html.ActionLink("Must have a int with int not", "MustHaveInt", "Home", new { }, new { @class = "btn btn-default" })</div>
  20:      </div>
  21:      <div class="row">
  22:          <div class="col-md-6">@Html.ActionLink("Can have a int with int set", "CanHaveInt", "Home", new { input=667 }, new { @class = "btn btn-default" })</div>
  23:      </div>
  24:      <div class="row">
  25:          <div class="col-md-6">@Html.ActionLink("Can have a int with int not set", "CanHaveInt", "Home", new { }, new { @class = "btn btn-default" })</div>
  26:      </div>
  27:      <hr/>
  28:      <div class="row">
  29:          <div class="col-md-6">@Html.ActionLink("Must have both set", "MustHaveBoth", "Home", new { text="plonie ogniosko", input=3}, new { @class = "btn btn-default" })</div>
  30:      </div>
  31:      <div class="row">
  32:          <div class="col-md-6">@Html.ActionLink("Must have both not", "MustHaveBoth", "Home", new { }, new { @class = "btn btn-default" })</div>
  33:      </div>
  34:      <div class="row">
  35:          <div class="col-md-6">@Html.ActionLink("Must have both set too much", "MustHaveBoth", "Home", new { text="a",input="123", x="co ja tu"}, new { @class = "btn btn-default" })</div>
  36:      </div>
  37:      <hr/>
  38:      <div class="row">
  39:          <div class="col-md-6">@Html.ActionLink("Greed only the using ones", "Greed", "Home", new { text = "a", input = "123" }, new { @class = "btn btn-default" })</div>
  40:      </div>
  41:      <div class="row">
  42:          <div class="col-md-6">@Html.ActionLink("Greed all the things", "Greed", "Home", new { text = "a", input = "123", wantSoMuch = -1, stillNotUsing = -1, itAll =-1}, new { @class = "btn btn-default" })</div>
  43:      </div>
  44:  </div>

Czyli razor w linkami do poszczególnych akcji, czasem w linku są wymagane argumenty czasem ich brak. Wygląda to tak:

Mała niespodzianka, która wyszła podczas pisania tego posta. Okazuje się, że gdy na liście argumentów jest string, ale nie zostanie przesłany i nawet jeśli nie posiada on domyślnej wartości to i tak pewne mechanizmy asp zadziałają i zostanie wywołana akcja zdefiniowana w kontrolerze. Ta sama sztuczka z int’em już nie zadziała. Zostanie wyświetlmy zółty ekran błędu, informujący o tym, że nie udało się połączyć wszystkich kropek i jest klops. Ale to nie o tym wpis – ot taka ciekawostka.

W kontrolerze możemy nadpisać wywołanie metody, np. takiej metody w taki sposób:

   1:  protected override void OnActionExecuting(ActionExecutingContext filterContext)
   2:  {
   3:      Debug.WriteLine(
   4:          string.Format("***** New Action *****n***** {0} *****", filterContext.ActionDescriptor.ActionName));
   5:      Debug.WriteLine("*** Action parameteres {0}***", filterContext.ActionParameters.Count);
   6:      filterContext.ActionParameters.ForEach(
   7:          ap => Debug.WriteLine(string.Format("Key: {0}tValue: {1}", ap.Key, ap.Value)));
   8:   
   9:      var queryString = filterContext.RequestContext.HttpContext.Request.QueryString;
  10:      Debug.WriteLine("*** Query string {0}***", queryString.AllKeys.Count());
  11:      queryString.AllKeys.ForEach(k =>
  12:          Debug.WriteLine(string.Format("Key: {0}tValue: {1}", k, queryString[k])));
  13:  }

I teraz docieramy do sedna wpisu, będziemy oglądać QueryString i ActionParameters. Dla starych wyjadaczy różnica pewnie jasna i oczywista, ja się poznałem z nimi dopiero ostatnio, oryginalni autorzy świetnie się spisali przeinaczając (jest takie słowo) ich cel: otóż, wymyślił sobie ktoś że skoro w projekcie jest bardzo dużo kontrolerów, to pewnie znajdzie się część wspólna, którą warto umieścić w jakimś bazowym dla projektu kontrolerze. Sanity check – OK. Wszystko mogło być dobrze, do momenty gdy ktoś inny, nie robiłem git blame, nie wpadł na pomysł aby sprawdzać czy argumenty się zgadzają i sprawdzać, czy action paramentes zawiera np projectId, który ma zostać wysłany od użytkownika. Sanity check – NOK. Każdy kontroler dziedziczący ten wspólny, musi mieć na liście argumentów każdej ze swojej akcji parametr “int projectId”. Co przeciwnym wypadku? Oczywiście że przekierowanie na InvalidUrl i uwaga, strona ta w żaden sposób nie informuje o tym, czego oczekiwała, ani nic. Po prostu invalid url. Sanity check – Reject!
Dlaczego mnie to tak drażni? Bo jest to ukrywanie zależności danej metody, tworzę albo pracuje z taki kodem i gdy na liście argumentów mam nie używane parametry, to je usuwam i oczekuje że kod nadal będzie działać.
Zobaczcie jak wygląda wywołanie poszczególnych linków:
***** New Action *****
***** MustHaveString *****
*** Action parameteres 1***
Key: text    Value: lorem ipsum
*** Query string 1***
Key: text    Value: lorem ipsum

***** New Action *****
***** MustHaveString *****
*** Action parameteres 1***
Key: text    Value:
*** Query string 0***

***** New Action *****
***** CanHaveString *****
*** Action parameteres 1***
Key: text    Value:
*** Query string 0***

***** New Action *****
***** CanHaveString *****
*** Action parameteres 1***
Key: text    Value: litwo ojczyzno moja
*** Query string 1***
Key: text    Value: litwo ojczyzno moja

***** New Action *****
***** MustHaveInt *****
*** Action parameteres 1***
Key: input    Value: 42
*** Query string 1***
Key: input    Value: 42

***** New Action *****
***** MustHaveInt *****
*** Action parameteres 1***
Key: input    Value:
*** Query string 0***

***** New Action *****
***** CanHaveInt *****
*** Action parameteres 1***
Key: input    Value: 667
*** Query string 1***
Key: input    Value: 667

***** New Action *****
***** CanHaveInt *****
*** Action parameteres 1***
Key: input    Value: 0
*** Query string 0***

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value: plonie ogniosko
Key: input    Value: 3
*** Query string 2***
Key: text    Value: plonie ogniosko
Key: input    Value: 3

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value:
Key: input    Value:
*** Query string 0***

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value: a
Key: input    Value: 123
*** Query string 3***
Key: text    Value: a
Key: input    Value: 123
Key: x    Value: co ja tu

***** New Action *****
***** Greed *****
*** Action parameteres 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value:
Key: stillNotUsing    Value:
Key: itAll    Value:
*** Query string 2***
Key: text    Value: a
Key: input    Value: 123

***** New Action *****
***** Greed *****
*** Action parameteres 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value: -1
Key: stillNotUsing    Value: -1
Key: itAll    Value: -1
*** Query string 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value: -1
Key: stillNotUsing    Value: -1
Key: itAll    Value: -1

O ile pierwsze z nich są w porządku, bo korzystają z tego czego wymagają, o tyle mały smród na końcu, niegodziwy zachłanny gostek chce aż pięciu parametrów, podczas gdy wykorzystuje tylko dwa z pięciu. Na szczęcie HomeController nie został jeszcze dotknięty ideą sprawdzania action parameters, ale jeśli ktoś wpadnie na pomysł napisania takiego kodu:

   1:  protected override void OnActionExecuting(ActionExecutingContext filterContext)
   2:  {
   3:      if (!filterContext.ActionParameters.Keys.Contains("itAll"))
   4:      {
   5:          Response.Redirect("http://jstadnicki.blogspot.com/");
   6:      }
   7:  }

To wtedy musicie dostarczyć mieć na liście argumentów metody, uwaga napiszę to jeszcze raz: sygnatura akcji musi mieć “itAll” na liście argumentów aby w ogóle się do niej dostać. Czyli wywołanie localhost/home/index?itAll=”dzialaj” nie zadziała, ponieważ index nie ma itAll na liście parametrów, a to że został on przesłany w query string nie pomoże.
Kończąc mój długi wpis, który miał być krótki. Nie ukrywajcie zależności w kodzie, zgłaszając błąd czy to użytkownikowi czy innemu programiście, podajcie wystarczająco dużo informacji, aby od razu jasne było dlaczego coś się nie udało. I dodając kod do klasy bazowej pamiętajcie o tym, jak wielki wpływ na inne części aplikacji będzie miała wasza zmiana. Dziedziczenie nie jest rozwiązaniem. I ostatnie jeszcze ActionParameters!!!!!!!!!!!!!!!=QueryString

Automapper NullSubstitue lubi cache

Ot taka ciekawostko z automappera, kto zna to niech czyta dalej, kto nie zna może zapoznać sie z poprzednim wpisem na tym właśnie blogu: http://jstadnickiag.nazwa.pl/jaroslawstadnickimainwebsite/?p=31.

Właściwa ciekawostka, mając taki kod:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:  Mapper.CreateMap<UserAccount, ProfilePrivateViewModel>()
   2:    .ForMember(d => d.Recruiter, o => o.NullSubstitute(new ProfilePrivateRecruiterViewModel()))
   3:    .ForMember(d => d.Developer, o => o.NullSubstitute(new ProfilePrivateDeveloperViewModel()));

Czyli jeśli rekruter lub developer będzie nullem, to chcemy aby zostało to zastąpione nowo tworzoną klasą. Wygląda prosto? Ci z was, którzy myślą tak jak ja, czyli przy każdym mapowaniu z UserAccount na ProfilePrivateViewModel będzie tworzony nowy obiekt rekrutera lub developera, jeśli w UserAccountnullem – są w błędzie. Otóż okazuje się, że automapper podczas inicjalizacji zapisuje sobie ten obiekt, a następnie przy każdym mapowaniu, gdy ma z niego skorzystać zwraca zawsze tą samą instancję obiektu. Jest to bardzo ważne, jeśli wy (ja też) modyfikujemy taką instancję. Czujecie już problem? Ponieważ będzie ona współdzielona pomiędzy inne nulle, gdy taki się pojawi i dostanie ten substytut, będzie on już zmodyfikowany.

Gdyby mieć jeszcze taki kod:

   1:  AfterMap((s, d) => d.Skills.Add(new DeveloperSkillViewModel())

Wyrwany trochę z kontekstu, ale rozumiecie idee. Okaże się, że po każdym mapowaniu z null na “nowy” viewmodel programisty, jego lista umiejętności rośnie o jeden. Dziesiąte mapowanie stworzy nowego developera, ale z dziesięcioma umiejętnościami.

Możecie się domyślić jak ja się dowiedziałem 😉

Pomimo tego ficzera, automapper dla mnie nadal dobrą biblioteką jest.

EmptyResult na zły sposób

Programując internety gdy wysyła się jakieś żądanie na serwer nie można założyć, że poleceni się po prostu wykona. Operacja void nie istnieje. Tzn można, ale to zła praktyka, można przecież wysłać żądanie i nie sprawdzić czy w ogóle doszło ono na serwer. Ale nie o to chodzi, mój przypadek polegał na tym, że wysyłać na serwer żądanie i chciałem tylko sprawdzić czy serwer to dostał czy nie. W moim przypadku wynik w ogóle nie był ważny. Naiwnie pomyślałem sobie, że wystarczy zwrócić (oczywiście w .net asp mvc) new EmptyResult() a na kliencie sprawdzić czy długość wyniku jest zero. Jak można się domyślić nie było by tego wpisu gdyby rzeczywistość nagięła się do mojego wyobrażenia.
Otóż pozytywny wynik operacji oraz długość wyniku była by zero gdybym wysłał request i ustawił dataType na script lub html. Ale nie w moim przypadku ja chciałem json. I co? Otóż biblioteka, która wrapowala żądania też była sprytna i czasem wysyłała żądanie json a czasem xml. I teraz operacja czasem działała gorzej a czasem jeszcze gorzej. Nie wiem czemu ubzdurałem sobie że EmptyResult coś zwróci i nie powinienem sprawdzić statusu odpowiedzi, zamiast zawartości PUSTEGO WYNIKU.

Ale jeśli jesteście ciekawi co odpowiada serwer na różne żądanie popatrzcie na ten przykładowy kod:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:  public class BlogController : Controller
   2:  {
   3:      [HttpGet]
   4:      public ActionResult Index()
   5:      {
   6:          return this.View("Index");
   7:      }
   8:   
   9:      [HttpGet]
  10:      public ActionResult CallMeToGetSuccessEmptyResult()
  11:      {
  12:          return new EmptyResult();
  13:      }
  14:  }

Następnie widok:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:   
   2:  <script src="~/Scripts/jquery-1.10.2.js"></script>
   3:  <div class="row">
   4:      @Html.ActionLink("By xml", "CallMeToGetSuccessEmptyResult", "Blog", null, new { @class = "btn btn-default test-button", id = "demo-xml", data_type_of_data = "xml", data_anchor_id = "#demo-container-xml" })
   5:      @Html.ActionLink("By json", "CallMeToGetSuccessEmptyResult", "Blog", null, new { @class = "btn btn-default test-button", id = "demo-json", data_type_of_data = "json", data_anchor_id = "#demo-container-json" })
   6:      @Html.ActionLink("By script", "CallMeToGetSuccessEmptyResult", "Blog", null, new { @class = "btn btn-default test-button", id = "demo-script", data_type_of_data = "script", data_anchor_id = "#demo-container-script" })
   7:      @Html.ActionLink("By html", "CallMeToGetSuccessEmptyResult", "Blog", null, new { @class = "btn btn-default test-button", id = "demo-html", data_type_of_data = "html", data_anchor_id = "#demo-container-html" })
   8:  </div>
   9:   
  10:  <div class="row">
  11:   
  12:      <div class="col-md-3">XML<div id="demo-container-xml"></div></div>
  13:      <div class="col-md-3">JSON<div id="demo-container-json"></div></div>
  14:      <div class="col-md-3">SCRIPT<div id="demo-container-script"></div></div>
  15:      <div class="col-md-3">HTML<div id="demo-container-html"></div></div>
  16:  </div>
  17:   
  18:  <script type="text/javascript">
  19:   
  20:      var showJson = function(data, status, anchor) {
  21:          $(anchor).html('');
  22:          var htmlToSet = "status: " + status + "<br/>";
  23:          if (data != null) {
  24:              htmlToSet += "data lenght: " + data.length;
  25:          } else {
  26:              htmlToSet += "data is null";
  27:          }
  28:              $(anchor).html( htmlToSet );
  29:      };
  30:   
  31:      var demoResult = function(e) {
  32:          e.preventDefault();
  33:          var href = $(e.target);
  34:          var data_type = href.data("type-of-data");
  35:          var anchor = href.data("anchor-id");
  36:          var ajaxOptions = {
  37:              url: '@Url.Action("CallMeToGetSuccessEmptyResult", "Blog")',
  38:              dataType: data_type,
  39:              success: function(data, status) {
  40:                  showJson(data, status, anchor);
  41:              },
  42:              error:function(data, status) {
  43:                  showJson(data, status, anchor);
  44:              }
  45:          };
  46:   
  47:          $.ajax(ajaxOptions);
  48:      };
  49:   
  50:      $(document).ready(function() {
  51:          $(".test-button").on("click", demoResult);
  52:      });
  53:   
  54:  </script>

I teraz na obrazu wyniki działania aplikaji:

Czyli nie zawsze EmptyResult to tylko “”, czasem to także parseerror gdy oczekujemy json. Na przyszłość polecam zastanowić się co się powinno sprawdzić, gdy nie chcemy nic sprawdzać. Oraz jak chce to sprawdzić.
Jak zawsze, chętnie popełnię kolejne błędy za was w następnym odcinku.

Redirect to anchor w asp mvc

Od jakiegoś czasu pracuje za prawdziwe złoto jako sieciowy programista, dawno nikt nie wymagał aby po przekierowaniu wrócić do jakiegoś specyficznego kawałka strony. Zawsze kończyło się przekierowaniem do pełnej. Zapomniałem już o takiej funkcjonalność, no prawie zapomniałem. Otóż klepiąc sobie coś tam w domu, chciałem po zrobieniu POSTa wrócić gdzieś na dół strony, akcja nie korzysta ze zdobyczy technologi jaką jest AJAX, więc strona się przeładowywuje. Pozostało mi tylko skorzystanie z elementu html, który posiada znacznik id oraz nawigacja do strony z podaniem tego znacznika w url. Znacznik podaje się po znaczku ‘#’. Kompilator nie chciał tego przyjąć do wiadomości, gdy próbowałem zrobić this.RedirectToAction(“Index”,”Home”,new{#=”comments”}); nie działało.
Na szczęście internety dają radę także i tym razem super hero w postaci stack overflow ułatwił życie.
Uwaga podaję odpowiedź:
this.Redirect(Url.RouteUrl(new { controller = “Home”, action = “Index”}) + “#comments”);
To tyle w tym krótkim odcinku. Jutro znowu do pracy.

A jak Pan klei stringi?

Człowiek idzie na rozmowę o pracę i pytają go o różne rzeczy, najczęściej pytają o to czego i tak nie będzie korzystać w tej pracy. Np, ile piłek golfowych mieści się w autobusie, czy ile jest okien w wąchocku, chociaż w takimi rzeczami lubi się parać HR. Nasi pytają o wzorce, gdzie najczęściej pada odpowiedź singleton i/lub fabryka. Wcześniej może zapytają o różnicę pomiędzy value type i reference type i kolejny klasyk to konkatenacja stringów. Każdy kto był chociaż na jednej rozmowie odpowie StringBuilder.
Jak często zdarza się wam łączyć taką ilość stringów, że tworzycie StringBuildera, żeby rzeczywiście było szybciej?. Ja chyba nigdy nie byłem (albo już nie pamiętam) w takiej sytuacji, ale zawsze mnie o to pytają. Po jednej z takich rozmów zacząłem rozmyślać co robię w takich sytuacjach, gdy mam kilka stringów do sklejenia, czy korzystam z łączenia stringów i o matko alokuje tworze ich kilka w pamięci raz na kilka sekund czy może tworze owego magicznego wszech potężnego Buildera, aby uratował mnie przy klejeniu “Ala ma 1 kota, koloru szarego“. Uświadomiłem sobie, że praktycznie zawsze korzystam ze string.Format. I co teraz? A jeśli on jest źle zaimplementowany i chłopaki z mikrosoftu kleją stringi jak przedszkolaki? Nie mogłem spać po nocy i musiałem sprawdzić. Dot peek na ratunek, sprawdziłem i jest pięknie, jestem uratowany, jest bohater, w środku siedzi StringBuilder i robi swoje. Także jeśli jesteście ciekawi co jest szybsze czy string.Format czy nowy StringBuilder to sprawdźcie sami. Ja w każdym bądź razie od teraz będę na rozmowach mówić, że korzystam ze string.Format().
Tyle, ot taki krótki wpis.