BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Wstęp do metadanych

22-09-2004 08:54 | ROBOC!K

Jedną z innowacji wprowadzonych na platformie .NET jest to, że złożenia (ang. assembly) są samoopisujące. Złożenie zawiera tzw. manifest złożenia, w którym przechowywane są liczne informacje:

  • nazwa, informacje kulturowe i wersja złożenia,

  • lista wszystkich plików należących do złożenia (złożenie nie musi zawierać się w jednym pliku),

  • lista powiązanych złożeń,

  • metadane.

Metadane zawierają opis wszystkich typów używanych w złożeniu, czyli wszystkie klasy wraz z metodami, właściwościami itd. Podobne rozwiązanie było już stosowane w obiektach COM (biblioteka typów). Jednak informacje zawarte w bibliotece typów były praktycznie niedostępne dla zwykłego programisty. Tymczasem platforma .NET dostarcza klas, zawartych w przestrzeni nazw System.Reflection, do odczytu metadanych.

Zalety metadanych:

  • Złożenia są samoopisujące – pliki wykonywalne posiadają wszystkie dane wymagane, aby mogły wchodzić w interakcję z innymi złożeniami.

  • Ułatwiają tworzenie komponentów – dzięki informacjom w metadanych możemy stworzyć komponent pewnym języku na platformę .NET i używać go w aplikacjach napisanych w innym języku.

  • Atrybuty – do opisu każdego typu możemy dołączać własne atrybuty, które będą dokładniej opisywały poszczególne elementy.

Sposobów wykorzystania informacji w metadanych jest wiele. Środowisko Visual Studio .NET wykorzystuje je np. w technologii IntelliSence. Gdy wpiszemy w oknie kodu nazwę obiektu i wciśniemy na klawiaturze klawisz kropki ‘.’, IDE wyświetli listę dostępnych metod i właściwości.

Aby przetestować dołączoną aplikację należy po skompilowaniu kodu i uruchomieniu programu wybrać w menu Plik polecenie Załaduj… i wskazać plik exe lub dll pod platformę .NET. Wskazanie „zwykłej” aplikacji spowoduje wyświetlenie błędu.

Zaczynamy

Tworzenie przeglądarki zaczynamy od stworzenia interfejsu. Będzie on zbliżony do Eksploratora Windows, czyli w lewym oknie struktura drzewiasta a w prawym lista ze szczegółami. W celu poprawienia funkcjonalności wykorzystamy kontrolkę Splitter, która umożliwia zmianę rozmiaru stosowanych kontrolek w trakcie działania programu. Po umieszczeniu tych trzech kontrolek na formatce należy je powiązać programowo. W tym celu rozwijamy region Windows Form Designer generated code i na końcu funkcji InitializeComponent umieszczamy poniższy kod:

     tree.Dock = DockStyle.Left;
     splitter1.Dock = DockStyle.Left;
     splitter1.MinExtra = 100;
     splitter1.MinSize = 75;
     lista.Dock = DockStyle.Fill;
     lista.View=View.Details;
     lista.Columns.Add("Nazwa",100,HorizontalAlignment.Left);
     lista.Columns.Add("Wartość",500,HorizontalAlignment.Left);
     Controls.AddRange(new Control[]{lista, splitter1, tree});
     tree.AfterSelect+=new TreeViewEventHandler(tree_AfterSelect);

Ważna jest tutaj kolejność dodawania kontrolek do formatki. Określa ona sposób działania okna dzielonego (splitter). Dalej dodajemy komponent menu i dodajemy do niego obsługę otwierania pliku:

private void menuItem2_Click(object sender, System.EventArgs e)
{
     if(dlgOpen.ShowDialog()==DialogResult.OK)
     {
          try
          {
               OpenAssembly(dlgOpen.FileName);
          }
          catch
          {
MessageBox.Show("Nie powiodło się odczytanie pliku.",
"Błąd otwarcia",MessageBoxButtons.OK,MessageBoxIcon.Error);
          }
     }
}

Teraz konkrety

Rozpoczynamy tworzenie aplikacji od zaimportowania na początek naszego pliku z kodem formatki - przestrzeni nazw System.Reflection, dzięki czemu przy każdym użyciu typów danych z tej przestrzeni nie będziemy musieli umieszczać nazwy przestrzeni przed nazwą typu:

using System.Reflection;

Klasą reprezentującą złożenie w pamięci jest Assembly. Aby odczytać plik złożenia z dysku, należy wywołać statyczną metodę Assembly.LoadFile, która to tworzy wypełniony obiekt. Kod funkcji OpenAssembly przedstawiono poniżej:

private void OpenAssembly(string filename)
{
     tree.Nodes.Clear();
     lista.Items.Clear();
TreeNode root=tree.Nodes.Add( Path.GetFileNameWithoutExtension( filename));
     Assembly file=Assembly.LoadFile(dlgOpen.FileName);
     root.Tag=file;
     ReadAssemblyTypes(root,file.GetTypes());
     //rozwiń gałąź główną
     root.Expand();
}

Ważna uwaga: wykorzystujemy własność Tag klasy TreeNode, dzięki czemu każda gałąź drzewka, która reprezentuje jakąś informację z metadanych, będzie od razu miała dostęp do obiektu, który zawiera tę informację. W powyższej funkcji gałąź główna drzewka przechowuje referencję do utworzonego obiektu klasy Assembly.

Następnym krokiem jest uzyskanie informacji, jakie typy danych zawiera złożenie. W tym celu wywołujemy funkcję Assembly.GetTypes(), która zwraca wszystkie typy danych w postaci tablicy Type[]. Odczytywaniem informacji z tablicy o poszczególnych typach zajmuje się funkcja ReadAssemblyTypes.

Kod tej funkcji ma postać:

private void ReadAssemblyTypes(TreeNode parentNode,Type[] assemblyTypes)
{
     TreeNode node,node1;
     foreach(Type typ in assemblyTypes)
     {
          node=new TreeNode(typ.Name);
          parentNode.Nodes.Add(node);
          node.Tag=typ;
          ReadAssemblyTypes(node,typ.GetNestedTypes());
          ReadProperties(node,typ.GetProperties());
          ReadFields(node,typ.GetFields());
          ReadMethods(node,typ.GetMethods());
          ReadConstructors(node,typ.GetConstructors());
          ReadEvents(node,typ.GetEvents());
          
          Type[] inters=typ.GetInterfaces();
          if(inters.Length>0)
          {
               node1=new TreeNode("Interfejsy");
               node.Nodes.Add(node1);
               ReadAssemblyTypes(node1,inters);
          }
     }
}

Funkcja ReadAssemblyTypes „przelatuje” przez wszystkie typy w tablicy i dla każdego z nich wywołuje odpowiednie funkcje, dzięki czemu otrzymujemy wiele informacji na temat danego typu. W programie ograniczyłem się jedynie do najważniejszych informacji, ale nic nie stoi na przeszkodzie, aby rozbudować przeglądarkę o kolejne typy.

Tabela: Metody klasy Assembly

Nazwa

Opis

GetNestedTypes()

Zwraca tablicę Type[] wszystkich zagnieżdżonych typów

GetProperties()

Zwraca tablicę PropertyInfo[] wszystkich właściwości z analizowanej klasy (struktury)

GetFields()

Zwraca tablicę FieldInfo[] wszystkich publicznych pól

GetMethods()

Zwraca tablicę MethodInfo[] wszystkich metod z danego typu

GetConstructors()

Zwraca tablicę ConstructorInfo[] wszystkich konstruktorów klasy

GetEvents()

Zwraca tablicę EventInfo[] wszystkich zdarzeń klasy

GetInterfaces()

Zwraca tablicę Type[] wszystkich zaimplementowanych interfejsów

 

Każda z utworzonych funkcji z przedrostkiem Read (np. ReadMethods, ReadFields, ReadEvents, ReadProperties itp.) zawiera podobny kod i realizuje podobne działania, dlatego pokrótce opiszę funkcję ReadMethod:

void ReadMethods(TreeNode parentNode,MethodInfo[] methods)

{
     TreeNode propNode=null;
     if(methods.Length>0)
     {
          propNode=new TreeNode("Metody");
          parentNode.Nodes.Add(propNode);
     }
     TreeNode node,paramNode;
     foreach(MethodInfo method in methods)
     {
          node=new TreeNode(method.Name);
          propNode.Nodes.Add(node);
          node.Tag=method;
          foreach(ParameterInfo param in method.GetParameters())
          {
               paramNode=new TreeNode(param.Name);
               node.Nodes.Add(paramNode);
               paramNode.Tag=param;
          }
     }
}

Na początku sprawdzamy, czy dla danego typu zdefiniowane są jakieś metody:

if(methods.Length>0)

i jeśli tak, to dodajemy gałąź do drzewka.

Następnie tworzymy gałąź dla każdej metody wyświetlając w drzewku jej nazwę:

     node=new TreeNode(method.Name);
     propNode.Nodes.Add(node);
     node.Tag=method;

Aby uzyskać dostęp do informacji o parametrach metod, wywołujemy metodę GetParameters() zwracającą obiekt ParameterInfo.

Ostatnim krokiem będzie zaimplementowanie funkcji wyświetlania informacji w liście szczegółów (w prawym oknie), która będzie realizowana po kliknięciu na jedną z pozycji w drzewku (w lewym oknie). Obsługujemy w tym celu zdarzenie AfterSelect kontrolki TreeView. Na początku czyścimy starą zawartość listy oraz odczytujemy obiekt opisujący zaznaczoną gałąź w drzewku:

     lista.Items.Clear();
     object tag=tree.SelectedNode.Tag;

teraz trzeba określić typ obiektu, czyli zdecydować, czy chcemy przeglądać informacje o metodzie, właściwości itp.

if(tag is Type)
{
          …
     }
     else if(tag is PropertyInfo)
     {
          …

Sam kod odczytu konkretnych informacji jest bardzo prosty. Aby uzyskać pomoc na temat wszystkich metod i własności klas MethodInfo itp. należy przejrzeć MSDN lub nawet za pomocą technologii IntelliSence można po nazwach określić, co zwraca dana właściwość.

Zakończenie

Metadane zawarte w złożeniu mogą być bardzo użyteczne. Poza standardowymi informacjami, w metadanych możemy umieszczać dodatkowe informacje za pomocą atrybutów.

Refleksje, oprócz prostego odczytywania informacji o obiektach, potrafią o wiele więcej. Możemy np. napisać kod, który przeanalizuje, jakie właściwości ma przekazany obiekt (znajdujący się w bibliotece napisanej przez innego programistę) i dynamiczne ustawienie wartości lub umożliwić użytkownikowi wpisanie do kontrolki TextBox nazwę metody, by za pomocą refleksji wywołać taką metodę.

Zainteresowanych tematem odsyłam do MSDN w indeksie „viewing type information”, „reflection” itp.

Do artykułu dołączony jest kod opisywanej aplikacji

Roman Podstawa

Załączniki:

Podobne artykuły

Komentarze 8

godlewskigo
godlewskigo
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
OK. Fajno. Opisano interfejs użytkownika. A gdzie METADANE? Tak? No wiem... napisane, ze to wstęp. To może dlatego. CYTUJĘ: "Sam kod odczytu konkretnych informacji jest bardzo prosty." MOJE PYTANIE: A czy interfejs użytkownika jest trudny? CYTUJĘ: "Aby uzyskać pomoc na temat wszystkich metod i własności klas MethodInfo itp. należy przejrzeć MSDN lub nawet za pomocą technologii IntelliSence można po nazwach określić, co zwraca dana właściwość" MOJE PYTANIE: To po co ten artykuł? Czytałem go, bym sie dowiedział, że mam sobie przejrzeć MSDN???? Toż to jakiś żart!?! A gdzie w takim razie adresy tych tajemniczych materiałów? Oj... ręce opadają.
User 79355
User 79355
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Autor rozminal sie z tematem. Niby metadane a tak naprawde opis jak stworzyc explorator windows. Nic ciekawego nie wnosi jesli chodzi o temat artykulu...
User 79215
User 79215
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Zastanawiałem się co napisać o tym artykule, ale kolega Maciek G. ujął to idealnie. Od siebie dodam jeszcze występowanie fatalnych błędów stylistycznych, które sprawiają, że niektóre zdania ciężko zrozumieć.
masta
masta
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
paranoja
User 80305
User 80305
31 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0
"Kfiatek Internetu 2004" w kategorii żart roku
User 79209
User 79209
2 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Za duzo kwiatkow typu: assembly - zlozenie. Opis tworzenia aplikacji jest zbedy, a info na temat metadanych jest bardzo, ale to bardzo okrojone.
testa1435
testa1435
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Aplikacja podoba mi się, do reszty sporo zastrzeżeń...
mokulnewicz8867
mokulnewicz8867
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Marne. W zasadzie tekst można by podsumować - "Zainteresowanych tematem odsyłam do MSDN w indeksie „viewing type information”, „reflection” itp."
pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.