Metadata och självbeskrivande komponenter
Tidigare kunde en programvarukomponent (.exe eller .dll) som skrevs på ett språk inte enkelt använda en programvarukomponent som skrevs på ett annat språk. COM tillhandahöll ett steg mot att lösa det här problemet. .NET gör komponentinteroperationen ännu enklare genom att tillåta kompilatorer att generera ytterligare deklarativ information till alla moduler och sammansättningar. Den här informationen, som kallas metadata, hjälper komponenter att interagera sömlöst.
Metadata är binär information som beskriver ditt program som lagras antingen i en PE-fil (Common Language Runtime Portable Executable) eller i minnet. När du kompilerar koden till en PE-fil infogas metadata i en del av filen och koden konverteras till CIL (Common Intermediate Language) och infogas i en annan del av filen. Varje typ och medlem som definieras och refereras till i en modul eller sammansättning beskrivs i metadata. När koden körs läser körningen in metadata i minnet och refererar till den för att identifiera information om kodens klasser, medlemmar, arv och så vidare.
Metadata beskriver varje typ och medlem som definierats i koden på ett språkneutralt sätt. Metadata lagrar följande information:
Beskrivning av sammansättningen.
Identitet (namn, version, kultur, offentlig nyckel).
De typer som exporteras.
Andra sammansättningar som den här sammansättningen är beroende av.
Säkerhetsbehörigheter som krävs för att köras.
Beskrivning av typer.
Namn, synlighet, basklass och gränssnitt implementerade.
Medlemmar (metoder, fält, egenskaper, händelser, kapslade typer).
Attribut.
- Ytterligare beskrivande element som ändrar typer och medlemmar.
Fördelar med metadata
Metadata är nyckeln till en enklare programmeringsmodell och eliminerar behovet av IDL-filer (Interface Definition Language), huvudfiler eller någon extern metod för komponentreferens. Med metadata kan .NET-språk beskriva sig själva automatiskt på ett språkneutralt sätt, vilket både utvecklaren och användaren inte kan se. Dessutom är metadata utökningsbara med hjälp av attribut. Metadata ger följande stora fördelar:
Självbeskrivande filer.
Vanliga språkkörningsmoduler och sammansättningar är självbeskrivande. En moduls metadata innehåller allt som behövs för att interagera med en annan modul. Metadata tillhandahåller automatiskt funktionerna i IDL i COM, så du kan använda en fil för både definition och implementering. Runtime-moduler och sammansättningar kräver inte ens registrering med operativsystemet. Därför återspeglar de beskrivningar som används av körningen alltid den faktiska koden i din kompilerade fil, vilket ökar programmets tillförlitlighet.
Språksamverkans och enklare komponentbaserad design.
Metadata innehåller all information som krävs om kompilerad kod så att du kan ärva en klass från en PE-fil som skrivits på ett annat språk. Du kan skapa en instans av alla klasser som skrivits på ett hanterat språk (vilket språk som helst som är avsett för den gemensamma språkkörningen) utan att behöva bekymra dig om explicit marshalling eller anpassad samverkanskod.
Attribut.
Med .NET kan du deklarera specifika typer av metadata, så kallade attribut, i din kompilerade fil. Attribut finns i hela .NET och används för att mer detaljerat kontrollera hur programmet fungerar vid körning. Dessutom kan du generera egna anpassade metadata till .NET-filer via användardefinierade anpassade attribut. Mer information finns i Attribut.
Metadata och PE-filstrukturen
Metadata lagras i ett avsnitt i en .NET-fil med bärbar körbar fil (PE), medan det gemensamma mellanliggande språket (CIL) lagras i ett annat avsnitt av PE-filen. Metadatadelen av filen innehåller en serie tabell- och heapdatastrukturer. CIL-delen innehåller CIL- och metadatatoken som refererar till metadatadelen av PE-filen. Du kan till exempel stöta på metadatatoken när du använder verktyg som IL Disassembler (Ildasm.exe) för att visa kodens CIL.
Metadatatabeller och heaps
Varje metadatatabell innehåller information om elementen i ditt program. En metadatatabell beskriver till exempel klasserna i koden, en annan tabell beskriver fälten och så vidare. Om du har tio klasser i koden har klasstabellen tiotals rader, en för varje klass. Metadatatabeller refererar till andra tabeller och heaps. Metadatatabellen för klasser refererar till exempel till tabellen för metoder.
Metadata lagrar också information i fyra heapstrukturer: sträng, blob, användarsträng och GUID. Alla strängar som används för namntyper och medlemmar lagras i strängens heap. En metodtabell lagrar till exempel inte namnet på en viss metod direkt, utan pekar på metodens namn som lagras i strängens heap.
Metadatatoken
Varje rad i varje metadatatabell identifieras unikt i CIL-delen av PE-filen med en metadatatoken. Metadatatoken liknar pekare, som finns kvar i CIL, som refererar till en viss metadatatabell.
En metadatatoken är ett fyrbytesnummer. Den översta byte anger den metadatatabell som en viss token refererar till (metod, typ och så vidare). De återstående tre byteen anger raden i metadatatabellen som motsvarar programmeringselementet som beskrivs. Om du definierar en metod i C# och kompilerar den till en PE-fil kan följande metadatatoken finnas i CIL-delen av PE-filen:
0x06000004
Den översta byte (0x06
) anger att detta är en MethodDef-token . De lägre tre byteen (000004
) anger att den gemensamma språkkörningen ska titta på den fjärde raden i Tabellen MethodDef för den information som beskriver den här metoddefinitionen.
Metadata i en PE-fil
När ett program kompileras för den vanliga språkkörningen konverteras det till en PE-fil som består av tre delar. I följande tabell beskrivs innehållet i varje del.
PE-avsnitt | Innehållet i PE-avsnittet |
---|---|
PE-rubrik | Indexet för PE-filens huvudavsnitt och adressen till startpunkten. Körningen använder den här informationen för att identifiera filen som en PE-fil och för att avgöra var körningen börjar när programmet läses in i minnet. |
CIL-instruktioner | Microsofts instruktioner för mellanliggande språk (CIL) som utgör din kod. Många CIL-instruktioner åtföljs av metadatatoken. |
Metadata | Metadatatabeller och heaps. Körningen använder det här avsnittet för att registrera information om varje typ och medlem i koden. Det här avsnittet innehåller även anpassade attribut och säkerhetsinformation. |
Körningsanvändning av metadata
För att bättre förstå metadata och dess roll i den gemensamma språkkörningen kan det vara bra att skapa ett enkelt program och illustrera hur metadata påverkar dess körningstid. I följande kodexempel visas två metoder i en klass med namnet MyApp
. Metoden Main
är programmets startpunkt, medan Add
metoden helt enkelt returnerar summan av två heltalsargument.
Public Class MyApp
Public Shared Sub Main()
Dim ValueOne As Integer = 10
Dim ValueTwo As Integer = 20
Console.WriteLine("The Value is: {0}", Add(ValueOne, ValueTwo))
End Sub
Public Shared Function Add(One As Integer, Two As Integer) As Integer
Return (One + Two)
End Function
End Class
using System;
public class MyApp
{
public static int Main()
{
int ValueOne = 10;
int ValueTwo = 20;
Console.WriteLine("The Value is: {0}", Add(ValueOne, ValueTwo));
return 0;
}
public static int Add(int One, int Two)
{
return (One + Two);
}
}
När koden körs läser körningen in modulen i minnet och läser in metadata för den här klassen. När den har lästs in utför körningen omfattande analys av metodens vanliga CIL-ström (intermediate language) för att konvertera den till snabba interna datorinstruktioner. Körningen använder en JIT-kompilator (just-in-time) för att konvertera CIL-instruktionerna till intern datorkod en metod i taget efter behov.
I följande exempel visas en del av den CIL som skapades från den tidigare kodens Main
funktion. Du kan visa CIL och metadata från alla .NET-program med hjälp av CIL Disassembler (Ildasm.exe).
.entrypoint
.maxstack 3
.locals ([0] int32 ValueOne,
[1] int32 ValueTwo,
[2] int32 V_2,
[3] int32 V_3)
IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldstr "The Value is: {0}"
IL_000b: ldloc.0
IL_000c: ldloc.1
IL_000d: call int32 ConsoleApplication.MyApp::Add(int32,int32) /* 06000003 */
JIT-kompilatorn läser CIL för hela metoden, analyserar den noggrant och genererar effektiva interna instruktioner för metoden. Vid IL_000d
påträffas en metadatatoken för Add
metoden (/*
06000003 */
) och körningen använder token för att konsultera den tredje raden i Tabellen MethodDef .
I följande tabell visas en del av tabellen MethodDef som refereras till av den metadatatoken som beskriver Add
metoden. Även om det finns andra metadatatabeller i den här sammansättningen och har egna unika värden, diskuteras endast den här tabellen.
Rad | Relativ virtuell adress (RVA) | ImplFlags | Flaggor | Name (Pekar på stränghap.) |
Signatur (pekar på blob-heap.) |
---|---|---|---|---|---|
1 | 0x00002050 | IL Hanterade |
Offentliga ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (konstruktor) | |
2 | 0x00002058 | IL Hanterade |
Offentliga Statisk ReuseSlot |
Huvud | String |
3 | 0x0000208c | IL Hanterade |
Offentliga Statisk ReuseSlot |
Lägg till | int, int, int |
Varje kolumn i tabellen innehåller viktig information om koden. Med RVA-kolumnen kan körningen beräkna startminnesadressen för den CIL som definierar den här metoden. Kolumnerna ImplFlags och Flags innehåller bitmasker som beskriver metoden (till exempel om metoden är offentlig eller privat). Kolumnen Namn indexerar namnet på metoden från strängens heap. Kolumnen Signatur indexerar definitionen av metodens signatur i blob-heapen.
Körningen beräknar önskad förskjutningsadress från RVA-kolumnen på den tredje raden och returnerar den här adressen till JIT-kompilatorn, som sedan fortsätter till den nya adressen. JIT-kompilatorn fortsätter att bearbeta CIL på den nya adressen tills den stöter på en annan metadatatoken och processen upprepas.
Med hjälp av metadata har körningen åtkomst till all information som behövs för att läsa in koden och bearbeta den till interna datorinstruktioner. På så sätt möjliggör metadata självbeskrivande filer och, tillsammans med det gemensamma typsystemet, arv mellan språk.