Најголемата придовка од Framework 2.0 е, без дискусија, воведувањето на генеричките типови, и заедно со нив типизираните листи. Наместо како во 1.1 да се става се и сешто во ArrayList, и да се троши многу време и енергија на boxing и unboxing, во 2.0 можеме лесно да си креираме List<Invoice>, List<Order>, List<WhateverWeNeed>, и да знаеме дека кога вадеме јаболки од кошничката, нема да мораме секојпат да ги убедуваме дека навистина се јаболки.

И природно, кога ги ставаме работите во низи, ни притребува да пребаруваме по нив, или да извршиме некоја акција на одреден дел од низата, или да селектираме подниза според некое својство или ... Наједноставниот начин е стариот добар for или foreach циклус:

List<Person> Writers;
...
foreach(Person person in Writers)
{
  if (person.IsDead)
  {
      Console.WriteLine("{1}, {0} ({2})",
		p.FirstName, p.LastName, p.IsDead?"Dead":"Living");
  }
}

Искрено, единствено подобрување од користење на ArrayList е тоа што сме сигурни дека секој person објект е навистина од класата Person. Затоа дизајнерите на .net платформата во List објектот ставиле неколку методи кои можат да ни помогнат баш со ваквите операции. Меѓу нив се:

  • Find, FindAll, FindIndex, FindLast, FindLastIndex - кои служат за пребарување низ листата, и се разликуваат објектот кој го враќаат и по насоката на пребарувањето
  • ForEach – метода која за секој објект од листат ќе изврши одредена акција
  • Sort – кој ја сортира листата според некој критериум
Параметарот кој го примаат овие методи е од тип delegate, т.е. кажано по старски, pointer кон метода. Овие методи мора точно да се слагаат по потпис со дефинираните, т.е. методите кои ќе се користат за Find мора да примаат еден параметар од тип T и да враќаат логична вредност. Да, звучи комплицирано, но во реалност изгледа вака:
public void Main
{
  List<Person> deadWriters=Writers.FindAll(DeadPersons);
  deadWriters.ForEach(PrintPerson);
}
public bool DeadPersons(Person p)
{
  return p.IsDead;
}
public void PrintPerson(Person p)
{
  Console.WriteLine("{1}, {0} ({2})",
		p.FirstName, p.LastName, p.IsDead?"Dead":"Living");
}
На методата за пребарување FindAll, како параметар се предава делегат кон методата DeadPersons. Оваа метода како параметер прима вредност од тип Person, а враќа логичка вредност. После извршувањето на FindAll во листата deadWriters ќе ги имаме сите Persons елементи за методата DeadPersons вратила вредност true.

Потоа за листата deadWriters ја повикуваме методата ForEach so делегат кон методата PrintPerson, со што ќе изпечатиме по еден ред со информации за секој од елементите на deadWriters листата.
Засега се е добро. Но, може да се забележи дека ниедна од методите кои ги користевме како делегати не прима никакви надворешни параметри, т.е комплетно се изолирани од надворешниот свет. Ако го разгледаме примерот за List.Find методата на MSDN, ќе видеме дека оваа тема и таму е културно заобиколена.

За да се постигне оваа функционалност има барем два начина. Првиот начин ќе им биде познат на сите оние кои се сеќаваат на GOSUB наредбата од BASIC. Нашите делегат методи имаат пристап до глобалните променливи, па можеме да ги искористеме нив да ни бидат “параметри” на методите, како во примерот

private string classLevelString;
public bool FirstNameStarts(Person p)
{
  return p.FirstName.StartsWith(classLevelString);
}
public void Main
{
  classLevelString = "A";
  List<Person> aWriters = Writers.FindAll(FirstNameStarts);
  aWriters.ForEach(PrintPerson);

  classLevelString = "R";
  List<Person> rWriters = Writers.FindAll(FirstNameStarts);
  rWriters.ForEach(PrintPerson);
}
Добрата страна на оваа техника е што функционира, и е релативно едноставна. Лошата страна е што ги крше правилата и логиката на објектно-ориентираниот дизајн, ефективно враќајќи не на технолошко ниво од рани ’80-ти.

Другиот метод, кој ги нема овие маки е да се користат анонимни делегати, наместо именувани методи. Детално за нив, наскоро.

До наредниот пат, ваш Sweko.