2011-07-10 00:00:00

Słów kilka o krotkach.

Chciałem dzisiaj opisać trochę moje doświadczenia z krotkami.

Pomimo że ich konstrukcja nie jest złożona, a istnienie klasy o ich cechać było wyczekiwane dość długo to pojawiły się one w .Net dopiero w wersji 4.0, a ich pojawienie się zostało zdecydowanie zagłuszone przez inne nowinki w tej wersji.

Pierwszym narzucającym się zastosowaniem dla krotek jest wielotypowana tablica. Tablica taka przydaje się gdy musimy przerzucić listę kilkuelementowych paczek obiektów pomiędzy dwoma miejscami w kodzie. Przykładem takiego miejsca wywoływanie operacji na bazie dla każdego rekordu zwróconego przez obiekt implementujące IDataReader. Jak wiadomo przy próbie wywołania zapytania przed zamknięciem Readera następuje zgłoszenie wyjątku. Problem taki rozwiązywało się na kilka sposobów (oczywiście nie polegających na rezygnowaniu z Readera):

1) Utworzenie kilku list dla każdego przepisywanego pola - powoduje konieczność stosowania pętli for zamiast foreach

2) Utworzenie listy tablic obiektów (listy słowników, lub innej wariacji generycznej) - fajne rozwiązanie dopuki nie potrzebna nam informacja o rzeczywistym typie elementu (wtedy trzeba rzutować).

3) Utworzenie klasy i przesyłanie listy obiektów tej klasy - C# to nie Java tutaj duża lisczba klas kuje w oczy ;]

4) Klasy anonimowe - byłyby idealne gdyby nie problem z nacechowaniem ich typem listy

5) Stosowanie elementu klasy stworzonej w innym celu (np. KeyValuePair<T1, T2>)

Zastosowanie krotek jest rozwiązaniem łączącym znaczną część zalet powyższych rozwiązań jednocześnie nie posiada większości ich wad:

List<Tuple<int, string>> tempList = new List<Tuple<int, string>>();
using(SqlCommand cmd = new SqlCommand())
{
	cmd.Connection = con;
	cmd.CommandText = "select id, nazwa from Tabela1";
	using(IDataReader reader = cmd.ExecuteReader())
	{
		while(reader.Read())
		{
			tempList.Add(new Tuple<int, string>());
		}
	}
}
using(SqlCommand cmd = new SqlCommand())
{
	cmd.Connection = con;
	cmd.CommandText = "insert into Tabela2(id, nazwa) values (@id, @nazwa)";
	foreach(var tuple in tempList)
	{
		cmd.AddWithValue("@id", tuple.Item1);
		cmd.AddWithValue("@nazwa", tuple.Item2);
		cmd.ExecuteNonQuery();
	}
}

Inną ciekawostką związaną z krotkami jest fakt iż mają solidnie zaprogramowane funkcje GetHashCode i Equals, przez co można je wykorzystać żeby ułatwić sobie implementację tych metod:

class ExampleClass
{
	public int IdPart1 { get; set; }
	public int IdPart2 { get; set; }

	public override int GetHashCode()
	{
		return new Tuple<int, int>(IdPart1, IdPart2).GetHashCode();
	}

	public override bool Equals(object obj)
	{
		if (obj is ExampleClass)
		{
			ExampleClass other = (ExampleClass)obj;
			return new Tuple<int, int>(IdPart1, IdPart2).Equals(new Tuple<int, int>(other.IdPart1, other.IdPart2));
		}
		return false;
	}
}

albo z wykorzystaniem LambdaEqualityComparer

class ExampleClass
{
	public int IdPart1 { get; set; }
	public int IdPart2 { get; set; }

	private LambdaEqualityComparer<ExampleClass, Tuple<int, int>> comparer = 
                     new LambdaEqualityComparer<ExampleClass, Tuple<int, int>>(ec => new Tuple<int, int>(ec.IdPart1, ec.IdPart2));

	public override int GetHashCode()
	{
		return comparer.GetHashCode(this);
	}

	public override bool Equals(object obj)
	{
		return comparer.Equals(this, obj as ExampleClass);
	}
}

Krotki implementują też interfejs IComparable co może być przydatne jeżeli chcielibyśmy zaimplementować ten interfejs w swoich klasach (prawie klasyczny przykład Dekoratora).

Kolejnymi interfejsami implementowanymi przez Krotki są IStructuralEquatable i IStructuralComparable jest to związane z silnym wykorzystywaniem ich w języku F# (niestety jeszcze do niego nie przysiadłem). Więcej informacji na ten temat można uzyskać od Marcina Najdera z firmy Comarch który silnie promuje ten język na konferencjach.

Ze względu na fakt iż krotki po zainicjowaniu nie zmieniają swojego stanu mają podobno zastosowanie w aplikacjach współbieżnych (podobno bo sam nie miałem okazji się o tym przekonać).

Na koniec chciałbym dodać kilka słów o wykrytych przeze mnie niedopracowaniach krotek.

Pierwsza niedogodność Nazwy Item1, Item2, Item3... często uniemożliwiają domyślenie się co jest co, jednak po pierwsze nie mam innego pomysłu na nazwy tych właściwości, po drugie z założenia powinny być one wykorzystywane jako tymczasowe pojemniki na obiekty.

Druga fakt iż nie da się ich łatwo zserializować i deserializować (co też nie jest ich poprawnym zastosowaniem).

W komentarzach proszę o umieszczanie pomysłów na inne zastosowanie dla krotek.

Tagi

C Sharp Programowanie

Komentarze:

2011-07-12 00:00:00

szogun1987

Problem z dodawaniem komentarzy naprawiony.