Metagegevens en zelfbeschrijfde onderdelen
In het verleden kon een softwareonderdeel (.exe of .dll) dat in één taal is geschreven, niet eenvoudig een softwareonderdeel gebruiken dat in een andere taal is geschreven. COM heeft een stap gegeven om dit probleem op te lossen. .NET maakt de interoperation van onderdelen nog eenvoudiger doordat compilers aanvullende declaratieve informatie kunnen verzenden naar alle modules en assembly's. Deze informatie, metagegevens genoemd, helpt onderdelen naadloos te communiceren.
Metagegevens zijn binaire informatie over uw programma dat is opgeslagen in een PE-bestand (Common Language Runtime Portable Executable) of in het geheugen. Wanneer u uw code compileert in een PE-bestand, worden metagegevens in één deel van het bestand ingevoegd en wordt uw code geconverteerd naar algemene tussenliggende taal (CIL) en ingevoegd in een ander gedeelte van het bestand. Elk type en lid waarnaar wordt verwezen in een module of assembly, worden beschreven in metagegevens. Wanneer code wordt uitgevoerd, laadt de runtime metagegevens in het geheugen en verwijst deze naar informatie over de klassen, leden, overname, enzovoort van uw code.
Metagegevens beschrijven elk type en lid dat in uw code is gedefinieerd op een taalneutrale manier. Met metagegevens worden de volgende gegevens opgeslagen:
Beschrijving van de assembly.
Identiteit (naam, versie, cultuur, openbare sleutel).
De typen die worden geëxporteerd.
Andere assembly's waarop deze assembly afhankelijk is.
Beveiligingsmachtigingen die nodig zijn om uit te voeren.
Beschrijving van typen.
Naam, zichtbaarheid, basisklasse en interfaces geïmplementeerd.
Leden (methoden, velden, eigenschappen, gebeurtenissen, geneste typen).
Kenmerken.
- Aanvullende beschrijvende elementen waarmee typen en leden worden gewijzigd.
Voordelen van metagegevens
Metagegevens zijn de sleutel tot een eenvoudiger programmeermodel en elimineert de noodzaak van IDL-bestanden (Interface Definition Language), headerbestanden of een externe methode van verwijzing naar onderdelen. Met metagegevens kunnen .NET-talen zichzelf automatisch op een taalneutrale manier beschrijven, onbeheerd door zowel de ontwikkelaar als de gebruiker. Daarnaast kunnen metagegevens worden uitgebreid via het gebruik van kenmerken. Metagegevens bieden de volgende belangrijke voordelen:
Zelfbeschrijfde bestanden.
Algemene taalruntimemodules en assembly's worden zelfbeschrijfd. De metagegevens van een module bevatten alles wat nodig is om met een andere module te communiceren. Metagegevens bieden automatisch de functionaliteit van IDL in COM, zodat u één bestand kunt gebruiken voor zowel definitie als implementatie. Runtimemodules en assembly's vereisen zelfs geen registratie met het besturingssysteem. Als gevolg hiervan weerspiegelen de beschrijvingen die door de runtime worden gebruikt, altijd de werkelijke code in het gecompileerde bestand, waardoor de betrouwbaarheid van toepassingen toeneemt.
Taalinteroperabiliteit en eenvoudiger ontwerp op basis van onderdelen.
Metagegevens bevatten alle informatie over gecompileerde code die u nodig hebt om een klasse over te nemen van een PE-bestand dat in een andere taal is geschreven. U kunt een exemplaar maken van elke klasse die is geschreven in elke beheerde taal (elke taal die gericht is op de algemene taalruntime) zonder dat u zich zorgen hoeft te maken over expliciet marshallen of het gebruik van aangepaste interoperabiliteitscode.
Kenmerken.
Met .NET kunt u specifieke soorten metagegevens, kenmerken genoemd, declareren in het gecompileerde bestand. Kenmerken zijn te vinden in .NET en worden gebruikt om in meer detail te bepalen hoe uw programma zich gedraagt tijdens runtime. Daarnaast kunt u uw eigen aangepaste metagegevens verzenden naar .NET-bestanden via door de gebruiker gedefinieerde aangepaste kenmerken. Zie Kenmerken voor meer informatie.
Metagegevens en de PE-bestandsstructuur
Metagegevens worden opgeslagen in één sectie van een PE-bestand (Portable Executable) van .NET, terwijl common intermediate language (CIL) wordt opgeslagen in een andere sectie van het PE-bestand. Het metagegevensgedeelte van het bestand bevat een reeks tabel- en heapgegevensstructuren. Het CIL-gedeelte bevat CIL- en metagegevenstokens die verwijzen naar het metagegevensgedeelte van het PE-bestand. U kunt metagegevenstokens tegenkomen wanneer u bijvoorbeeld hulpprogramma's zoals IL Disassembler (Ildasm.exe) gebruikt om de CIL van uw code weer te geven.
Metagegevenstabellen en heaps
Elke metagegevenstabel bevat informatie over de elementen van uw programma. In een tabel met metagegevens worden bijvoorbeeld de klassen in uw code beschreven, in een andere tabel worden de velden beschreven, enzovoort. Als u tien klassen in uw code hebt, heeft de klastabel tientallen rijen, één voor elke klasse. Metagegevenstabellen verwijzen naar andere tabellen en heaps. De metagegevenstabel voor klassen verwijst bijvoorbeeld naar de tabel voor methoden.
Metagegevens slaan ook informatie op in vier heap-structuren: tekenreeks, blob, gebruikerstekenreeks en GUID. Alle tekenreeksen die worden gebruikt om typen en leden te noemen, worden opgeslagen in de heap van de tekenreeks. Een methodetabel slaat bijvoorbeeld niet rechtstreeks de naam van een bepaalde methode op, maar verwijst naar de naam van de methode die is opgeslagen in de tekenreeks heap.
Metagegevenstokens
Elke rij van elke metagegevenstabel wordt uniek geïdentificeerd in het CIL-gedeelte van het PE-bestand door een metagegevenstoken. Metagegevenstokens zijn conceptueel vergelijkbaar met aanwijzers, die worden bewaard in CIL, die verwijzen naar een bepaalde metagegevenstabel.
Een metagegevenstoken is een vier-bytenummer. De bovenste byte geeft de metagegevenstabel aan waarnaar een bepaald token verwijst (methode, type, enzovoort). De resterende drie bytes geven de rij in de metagegevenstabel op die overeenkomt met het programmeerelement dat wordt beschreven. Als u een methode in C# definieert en deze compileert in een PE-bestand, bestaat het volgende metagegevenstoken mogelijk in het CIL-gedeelte van het PE-bestand:
0x06000004
De bovenste byte (0x06
) geeft aan dat dit een MethodDef-token is. De lagere drie bytes (000004
) vertelt de runtime van de algemene taal om in de vierde rij van de MethodDef-tabel te zoeken voor de informatie die deze methodedefinitie beschrijft.
Metagegevens binnen een PE-bestand
Wanneer een programma wordt gecompileerd voor de algemene taalruntime, wordt het geconverteerd naar een PE-bestand dat uit drie onderdelen bestaat. In de volgende tabel wordt de inhoud van elk onderdeel beschreven.
PE-sectie | Inhoud van PE-sectie |
---|---|
PE-koptekst | De index van de hoofdsecties van het PE-bestand en het adres van het toegangspunt. De runtime gebruikt deze informatie om het bestand te identificeren als een PE-bestand en om te bepalen waar de uitvoering begint bij het laden van het programma in het geheugen. |
CIL-instructies | De tussenliggende taalinstructies van Microsoft (CIL) waaruit uw code bestaat. Veel CIL-instructies worden vergezeld van metagegevenstokens. |
Metagegevens | Metagegevenstabellen en heaps. De runtime gebruikt deze sectie om informatie over elk type en lid in uw code vast te leggen. Deze sectie bevat ook aangepaste kenmerken en beveiligingsgegevens. |
Runtimegebruik van metagegevens
Het kan handig zijn om een eenvoudig programma te maken en te laten zien hoe metagegevens van invloed zijn op de runtime van de algemene taal. In het volgende codevoorbeeld ziet u twee methoden in een klasse met de naam MyApp
. De Main
methode is het beginpunt van het programma, terwijl de Add
methode simpelweg de som van twee gehele getallen retourneert.
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);
}
}
Wanneer de code wordt uitgevoerd, laadt de runtime de module in het geheugen en raadpleegt de metagegevens voor deze klasse. Na het laden voert de runtime een uitgebreide analyse uit van de CIL-stroom (Common Intermediate Language) van de methode om deze te converteren naar snelle systeemeigen machine-instructies. De runtime maakt gebruik van een Just-In-Time-compiler (JIT) om de CIL-instructies te converteren naar systeemeigen machinecode één methode tegelijk, indien nodig.
In het volgende voorbeeld ziet u een deel van het CIL dat is geproduceerd op basis van de functie van Main
de vorige code. U kunt de CIL en metagegevens van elke .NET-toepassing bekijken met behulp van de CIL-demonteerfunctie (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 */
De JIT-compiler leest de CIL voor de hele methode, analyseert deze grondig en genereert efficiënte systeemeigen instructies voor de methode. Er IL_000d
wordt een metagegevenstoken voor de Add
methode (/*
06000003 */
) aangetroffen en de runtime gebruikt het token om de derde rij van de MethodDef-tabel te raadplegen.
In de volgende tabel ziet u een deel van de MethodDef-tabel waarnaar wordt verwezen door het metagegevenstoken waarin de Add
methode wordt beschreven. Hoewel er andere metagegevenstabellen in deze assembly bestaan en hun eigen unieke waarden hebben, wordt alleen deze tabel besproken.
Rij | Relatief virtueel adres (RVA) | ImplFlags | Vlaggen | Naam (Wijst naar tekenreeks heap.) |
Handtekening (Verwijst naar blob heap.) |
---|---|---|---|---|---|
1 | 0x00002050 | IL Beheerd |
Openbaar ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (constructor) | |
2 | 0x00002058 | IL Beheerd |
Openbaar Statisch ReuseSlot |
Hoofd | String |
3 | 0x0000208c | IL Beheerd |
Openbaar Statisch ReuseSlot |
Toevoegen | int, int, int |
Elke kolom van de tabel bevat belangrijke informatie over uw code. Met de RVA-kolom kan de runtime het begingeheugenadres van de CIL berekenen waarmee deze methode wordt gedefinieerd. De kolommen ImplFlags en Vlaggen bevatten bitmaskers die de methode beschrijven (bijvoorbeeld of de methode openbaar of privé is). De kolom Naam indexeert de naam van de methode uit de heap van de tekenreeks. De kolom Handtekening indexeert de definitie van de handtekening van de methode in de blob-heap.
De runtime berekent het gewenste offsetadres van de RVA-kolom in de derde rij en retourneert dit adres aan de JIT-compiler, die vervolgens naar het nieuwe adres gaat. De JIT-compiler blijft CIL op het nieuwe adres verwerken totdat het een ander metagegevenstoken tegenkomt en het proces wordt herhaald.
Met behulp van metagegevens heeft de runtime toegang tot alle informatie die nodig is om uw code te laden en te verwerken in systeemeigen machine-instructies. Op deze manier maakt metagegevens het mogelijk om bestanden zelf te beschrijven en, samen met het algemene typesysteem, cross-language overname.