Witaj na blogu

Witaj na moim blogu, który powstał z potrzeby chwili i mam nadzieję będzie długo istniał i dostarczał wiedzy i uciechy najróżniejszym internetowym indywiduom :). Zamierzam zamieszczać tutaj wymyślone lub wygrzebane przeze mnie rozwiązania problemów algorytmicznych i programistycznych. Możesz też natknąć się na szczyptę humoru lub moich osobistych przemyśleń (nie biorę odpowiedzialności za efekty czytania). Korzystaj z tagów - znajdziesz (być może) to czego szukasz...

piątek, 6 kwietnia 2012

Refleksja - część 1

Ponieważ ostatnio często korzystam z mechanizmu refleksji w C# (.NET) postanowiłem napisać parę słów o tymże mechanizmie i może zamieścić jakieś przykłady w kolejnych częściach. Ale co to w ogóle jest ta refleksja?

Otóż refleksja jest mechanizmem, który umożliwia dostęp do metadanych klasy lub obiektu. Mówiąc po ludzku, korzystając z refleksji, możemy dostać się do pól, właściwości i metod klasy, albo do jej atrybutów. Osobiście najczęściej korzystam z atrybutów, którymi zajmę się na początku.

Otóż atrybuty, są specjalnymi klasami przenoszącymi metadane innych obiektów. Każdy nowy atrybut dziedziczy po klasie Attribute i konwencją jest, że nazwy klas atrybutów kończą się słowem Attribute. Co może zawierać taki atrybut? Każde dane jakie chcemy przypiąć do klasy, jej pól lub właściwości, metod itd, a które będziemy chcieli wyciągnąć w trakcie działania programu. Pracując nad własnym mechanizmem ORM, utworzyłem klasy do przechowywania danych z bazy danych, które dziedziczyły po interfejsie IDBEntity. Klasy te są zbudowane w taki sposób, że posiadają dokładnie te same pola co odpowiadające ima tabele w bazie danych. Wszystko pięknie, ale teraz żeby dowolny rekord odczytać, zapisać lub uaktualnić, muszę w każdej klasie zamieścić odpowiednie metody w których będzie specyficzny dla danej klasy SQL. Trochę z tym dużo roboty, więc posłużmy się atrybutami i mechanizmem refleksji:

  1. Wyposażamy klasę w atrybut opisujący do jakiej tabeli się odnosi
  2. Wyposażamy jej pola (właściwości) w atrybuty opisujące da jakich kolumn w tabeli bazodanowej się odnoszą
  3. Tworzymy klasę statyczną, która na podstawie samego typu klasy, zwróci nam kolekcję rekordów pobranych z odpowiedniej tabeli, a rekordy te będą klasami podanego typu.
NAJPIERW DEKLARUJEMY ATRYBUTY

public class DBTableAttribute : Attribute {
public string TableName { private set; get; }

public DBTableAttribute(string tname){
TableName = tname;
}
}

public class DBFieldAttribute : Attribute {
public string FieldName { private set; get; }
public SQLDbType DBType { private set; get; }
public DBFieldAttribute (string fname, SQLDbType ftype){
FieldName = fname;
DBType = ftype;
}
}

Atrybuty zadeklarowane ale jak ich użyć? Bardzo prosto. Atrybut wpisujemy w nawiasach kwadratowych przed deklaracją elementu do którego się odnoszą:

[DBTable("osoby")] //to jest nasz atrybut
public class DBOsoby : IDBEntity {
[DBField("numer",DQLDBType.Int32)]
public int nr { private set; get; }
...
...
//tutaj jeszcze konstruktory i metody dziedziczone z interfejsu
}

Teraz możemy już odczytać metadane naszej klasy we wspomnianej wcześniej klasie statycznej, ale to już w następnej części...

Jestem dupa nie bloger

Rozpocząłem tego bloga i o nim zapomniałem, co działo się i działo. Teraz skoro już się wydziało, to może wrócę i znowu zacznę pisać z jakimi problemami się spotykam i jak je rozwiązuję (albo i nie). Kilka ciekawych tematów się pojawiło, więc o czym pisać jest. Czasem tylko czasu brakuje...

piątek, 29 stycznia 2010

Algorytmy grafowe - algorytm Kruskala

Algorytm Kruskala, jest algorytmem służącym do wyznaczania minimalnego drzewa spinającego grafu nieskierowanego. Poniższa implementacja w C# pochodzi z mojego projektu zaliczeniowego z przedmiotu "Algorytmy i struktury danych".

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Grafy
{
class Program
{
static Graf g;
static bool jestGraf = false;

static void Main(string[] args)
{
string c = "";

RysujMenu();

while (c != "x")
{
RysujMenu();
c = Console.ReadLine();
switch (c)
{
case "a" :
DodajNowyGraf();
break;
case "c" :
CzyscGraf();
break;
case "k" :
DodajKrawedzDoGrafu();
break;
case "z" :
Zapisz();
break;
case "w" :
g = new Graf(1);
Czytaj();
jestGraf = true;
break;
case "u" :
UsunKrawedzGrafu();
break;
case "d" :
WyznaczDrzewo();
break;
}
}
}

private static void WyznaczDrzewo()
{
Console.WriteLine("Wyznaczam mnimalne drzewo spinające:");
//częśc wykonawcza
Las l = new Las(g.ileWierzcholkow);
foreach (Krawedz k in g.krawedzie)
{
l.SprawdzKrawedz(k);
}

if (l.drzewa.Count > 1)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Graf nie jest spójny!");
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.White;
}
else
{
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.Yellow;
foreach (Krawedz k in l.drzewa[0].krawedzie)
{
Console.WriteLine("[{0,3},{1,3},{2,3}]", k.WierzcholekPoczatkowy.ToString(), k.WierzcholekKoncowy.ToString(), k.Waga.ToString());
}
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.White;
}

Console.WriteLine("Naciśnij ENTER aby przejśc do menu");
Console.ReadLine();
}

private static void Czytaj()
{
string na;
Console.WriteLine("Podaj nazwę pliku");
na = Console.ReadLine();
g.CzytajZPliku(na);
}

private static void Zapisz()
{
string na;
Console.WriteLine("Podaj nazwę pliku");
na = Console.ReadLine();
g.ZapiszDoPliku(na);
}

private static void UsunKrawedzGrafu()
{
string wp;
string wk;
Console.WriteLine("Podaj wierzchołek początkowy");
wp = Console.ReadLine();
Console.WriteLine("Podaj wierzchołek końcowy");
wk = Console.ReadLine();
g.UsunKrawedz(int.Parse(wp), int.Parse(wk));
}

private static void CzyscGraf()
{
g = null;
jestGraf = false;
}

private static void DodajNowyGraf()
{
string c;
Console.WriteLine("Podaj ilośc wierzchołków grafu:");
c = Console.ReadLine();
g = new Graf(int.Parse(c));
jestGraf = true;
}

public static void DodajKrawedzDoGrafu()
{
int pierwszy;
int drugi;
int waga;

string c;

Console.WriteLine("Podaj pierwszy wierzchołek");
c = Console.ReadLine();
pierwszy = int.Parse(c);
Console.WriteLine("Podaj drugi wierzchołek");
c = Console.ReadLine();
drugi = int.Parse(c);
Console.WriteLine("Podaj wage");
c = Console.ReadLine();
waga = int.Parse(c);

Krawedz kr = new Krawedz(pierwszy, drugi, waga);

g.DodajKrawedz(kr);
}

public static void RysujMenu()
{
Console.Clear();
Console.WriteLine("Program \"Grafy\" - Paweł Filipowski IMUZ 1.1 (2009)");
if (jestGraf)
{
Console.WriteLine("Utworzono graf o {0} wierzchołkach. W grafie jest {1} krawędzi.", g.ileWierzcholkow, g.krawedzie.Count.ToString());
PokazKrawedzie();
}
Console.WriteLine();
if (!jestGraf)
{
Console.WriteLine("[a] Nowy graf");
Console.WriteLine("[w] Wczytaj z pliku");
}
else
{
Console.WriteLine("[k] Dodaj krawędź");
Console.WriteLine("[u] Usuń krawędź");
Console.WriteLine("[z] Zapisz do pliku");
Console.WriteLine("[c] Czyśc graf");
Console.WriteLine("[d] Wyznacz minimalne drzewo spinające algorytmem Kruskala");
}
Console.WriteLine("[x] Wyjście");

}

private static void PokazKrawedzie()
{
foreach (Krawedz k in g.krawedzie)
{
Console.WriteLine("[{0,3},{1,3},{2,3}]", k.WierzcholekPoczatkowy.ToString(), k.WierzcholekKoncowy.ToString(), k.Waga.ToString());
}
}
}
}

Deklaracje klas

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Grafy
{
class Krawedz : IComparable
{
public int WierzcholekPoczatkowy;
public int WierzcholekKoncowy;
public int Waga;

public Krawedz(int wp, int wk, int w)
{
this.WierzcholekPoczatkowy = wp;
this.WierzcholekKoncowy = wk;
this.Waga = w;
}

int IComparable.CompareTo(Object obj)
{
if (obj is Krawedz) //sprawdzenie czy obiekt jest prawidłowego typu
{
Krawedz castObj = (Krawedz)obj; //konieczne rzutowanie typu
return this.Waga.CompareTo(castObj.Waga);
}
throw new ArgumentException("Obiekt nie jest Krawędzią");
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Grafy
{
class Graf
{
public int ileWierzcholkow;
public List krawedzie;

public Graf(int iw)
{
this.ileWierzcholkow = iw;
krawedzie = new List();
}

public void DodajKrawedz(Krawedz k)
{
krawedzie.Add(k);
krawedzie.Sort();
}

public void UsunKrawedz(Krawedz k)
{
krawedzie.Remove(k);
}

public bool UsunKrawedz(int wp, int wk)
{
bool jestKrawedz = false;
foreach (Krawedz k in krawedzie)
{
if (k.WierzcholekPoczatkowy == wp && k.WierzcholekKoncowy == wk)
{
jestKrawedz = true;
krawedzie.Remove(k);
break;
}
}
return jestKrawedz;
}

public void ZapiszDoPliku(string nazwa)
{
StreamWriter x = new StreamWriter(nazwa, false, Encoding.UTF8);
x.WriteLine(this.ileWierzcholkow);
x.WriteLine(this.krawedzie.Count());
foreach (Krawedz k in this.krawedzie)
{
x.Write(k.WierzcholekPoczatkowy);
x.Write(",");
x.Write(k.WierzcholekKoncowy);
x.Write(",");
x.WriteLine(k.Waga);
}
x.Dispose();
}

public void CzytajZPliku(string nazwa)
{
string line;
string wp;
string wk;
string w;
StreamReader x = new StreamReader(nazwa, Encoding.UTF8);
line = x.ReadLine(); //czytanie liczby wierzchołkow
this.ileWierzcholkow = int.Parse(line);
line = x.ReadLine(); //czytanie liczby krawędzi (w sumie nie do końca potrzebne)
line = x.ReadLine();
while(line != null)
{
wp = line.Substring(0, line.IndexOf(","));
line = line.Substring(line.IndexOf(",") + 1);
wk = line.Substring(0, line.IndexOf(","));
line = line.Substring(line.IndexOf(",") + 1);
w = line;

Console.WriteLine(wp + "/" + wk + "/" + w);
//Console.ReadLine();

Krawedz k = new Krawedz(int.Parse(wp), int.Parse(wk), int.Parse(w));


this.DodajKrawedz(k);
line = x.ReadLine();
}
x.Dispose();
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Grafy
{
class Drzewo
{
public List wierzcholki;
public List krawedzie;

public Drzewo(int nrwierzcholka)
{
this.wierzcholki = new List();
this.krawedzie = new List();
wierzcholki.Add(nrwierzcholka); //na początku drzewo składa się z jednego wierzcholka
}

public void DodajDrzewo(Drzewo d)
{
this.wierzcholki.AddRange(d.wierzcholki);
this.krawedzie.AddRange(d.krawedzie);
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Grafy
{
class Las
{
public List drzewa;

//tworzenie lasu pustych wierzcholkow
public Las(int iledrzew) {
this.drzewa = new List();
for(int i = 1; i <= iledrzew; i++) {
drzewa.Add(new Drzewo(i));
}
}

//zwraca drzewo w którym istnieje zadany wierzchołek
public Drzewo SzukajWierzcholka(int wierzcholek)
{
Drzewo wynik = null;
foreach (Drzewo d in drzewa)
{
if (d.wierzcholki.Contains(wierzcholek))
{
wynik = d;
break;
}
}
return wynik;
}

//łączy drzewa -> dołącza drugie do pierwszego
public void PolaczDrzewa(Drzewo d1, Drzewo d2)
{
foreach (int w in d2.wierzcholki)
{
d1.wierzcholki.Add(w);
}

foreach (Krawedz k in d2.krawedzie)
{
d1.krawedzie.Add(k);
}
drzewa.Remove(d2);
}

//dołącza krawędź do drzewa jeśli jest taka możliwośc
public void SprawdzKrawedz(Krawedz k)
{
Drzewo d1 = SzukajWierzcholka(k.WierzcholekPoczatkowy);
Drzewo d2 = SzukajWierzcholka(k.WierzcholekKoncowy);
if (!d1.Equals(d2))
{
d1.krawedzie.Add(k);
PolaczDrzewa(d1, d2);
}
}
}
}


Serializacja binarna w C#

Czas jakiś temu nurtował mnie problem przy przenoszeniu mechanizmu magazynowania danych z Delphi na C#. Ponieważ Delphi działa troszeczkę inaczej, okazało się że gdybym chciał przenieść mój mechanizm w formie niezmienionej, spędziłbym długie godziny na oprogramowywaniu tegoż. Jednak pojawiło się światełko w tunelu a zwie się ono SERIALIZACJA.

Na czym też polega owa serializacja? Otóż platforma .NET dostarcza nam ciekawego mechanizmu zwanego atrybutami. Nadanie atrybutu jakiejś klasie, daje możliwość wyczyniania z nią różnych rzeczy przez inne klasy. A jak to ma się do tematu? Istnieje atrybut [Serializable], który dołączony do klasy powoduje iż istnieje możliwość jej serializacji w dwóch typach - do pliku XML oraz do strumienia binarnego. A sama serializacja powoduje zapisanie wszystkich danych (stanu) przechowywanych w klasie poddanej temu zabiegowi. Co ciekawsze, jeżeli jakaś klasa załóżmy nazywająca się Samochod która sama posiada atrybut Serializable, zawiera w sobie listę obiektów klasy Czesc która również posiada ten atrybut, to przy serializacji klasy Samochod zostaną automatycznie zserializowane wszystkie wystąpienia klasy Czesc. Ale przejdmy do tego jak działa ten mechanizm.

Utwórzmy zatem klasę, którą można będzie bezkarnie zserializować...

[Serializable]
class MojaKlasa {

int a;
string b;
float c;

//prosty konstruktor który ustawia stan klasy
MojaKlasa(int pa, string pb, float pc) {
this.a = pa;
this.b = pb;
this.c = pc;
}
}

Do zserializowania obiektu potrzebujemy innego obiektu klasy BinaryFormatter znajdującego się w przestrzeni nazw System.Serialization.Formatters.Binary . Musimy również dołączyć przestrzeń nazw System.Serialization aby można było nadać atrybut. W kodzie import tych przestrzeni wygląda następująco:

using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

Ponieważ jednak BinaryFormatter jako parametr serializacji przyjmuje strumień, musimy zdecydować się w jakim strumieniu umieścimy nasz zserializowany obiekt. Najprostszym sposobem jest umieścić nasz obiekt w pliku, więc utwórzmy strumień plikowy. Klasy potrzebne do jego utworzenia znajdują się w przestrzeni nazw System.IO. Deklaracja takiego strumienia wygląda następująco:

FileStream fs = new FileStream(@"mojplik.bin", FileMode.Create);

Powyższa linia kodu mówi tyle co: Utwórz strumień plikowy, który będzie przypisany do pliku "mojplik.bin" w trybie tworzenia (czyli jeżeli istnieje taki plik to go nadpisz).

Mamy już przygotowany strumień, teraz czas zabrać się za serializację.

BinaryFormatter bf = new BinaryFormatter();

Utworzyliśmy nasz formater, ale jak teraz zserializować klasę? Po pierwsze utwórzmy jej obiekt:

MojaKlasa mk = new MojaKlasa(1,"Jakiś napis",3.14f);

Mamy już świeży obiekt naszej klasy, na dodatek wypełniony danymi. Przystąpmy zatem do serializacji:

bf.Serialize(fs,mk);
fs.Close();

Tym samym zserializowaliśmy binarnie naszą klasę. Druga linia powoduje zamknięcie strumienia plikowego, co skutkuje fizycznym zapisem pliku na dysk.

I oto cała serializacja. Dość proste. W najbliższym czasie rozbudujemy trochę ten przykład, tak żeby dodatkowo zaszyfrować i spakować zserializowany obiekt.

Mam nadzieję że ten post będzie przydatny.

No to zaczynajmy...

Szukałem ostatnio różnych informacji. Umiarkowanie intensywnie, ale odpytywanie wujka googla nie na wiele się zdało. Dlatego stwierdziłem, że może założę bloga. Opublikuję na nim różne przydatne informacje dotyczące programowania, tworzenia stron internetowych i innych dziwnych rzeczy. A może nawet jakieś swoje przemyślenia opiszę. Zobaczymy...

Obserwatorzy