Frage

Bearbeiten : Kommentare unten. Auch diese .


Hier ist, was mich irgendwie verwirrend. Mein Verständnis ist, dass wenn ich eine Enumeration wie dieses ...

enum Animal
{
    Dog,
    Cat
}

... was habe ich im Wesentlichen getan wird definiert einen Werttyp genannt Animal mit zwei definierten Werten, Dog und Cat. Diese Art stammt aus dem Referenztyp System.Enum (etwas, das Werttyp kann normalerweise nicht, zumindest nicht in C # -aber die in diesem Fall erlaubt ist), und hat eine Anlage zur Rück Gießen und her zu / von int Werten.

Wenn die Art, wie ich gerade beschrieben den Aufzählungstyp oben wahr wäre, dann würde ich den folgenden Code erwartet eine InvalidCastException zu werfen:

public class Program
{
    public static void Main(string[] args)
    {
        // Box it.
        object animal = Animal.Dog;

        // Unbox it. How are these both successful?
        int i = (int)animal;
        Enum e = (Enum)animal;

        // Prints "0".
        Console.WriteLine(i);

        // Prints "Dog".
        Console.WriteLine(e);
    }
}

Normalerweise Sie können unbox keinen Wert Typen von System.Object als etwas anderes als seine genauen Art . wie die oben möglich ist? Es ist, als ob der Animal Typ ein int (nicht nur Cabrio zu int) und ein Enum (nicht nur Cabrio zu Enum) zur gleichen Zeit. Ist es Mehrfachvererbung? Hat System.Enum irgendwie erben von System.Int32 (etwas, was ich hätte möglich sein, nicht zu erwarten)?

Bearbeiten : Es kann nicht eine der oben sein. Der folgende Code zeigt dies (glaube ich) schlüssig:

object animal = Animal.Dog;

Console.WriteLine(animal is Enum);
Console.WriteLine(animal is int);

Die obigen Ausgänge:

True
False

Sowohl die MSDN-Dokumentation auf Aufzählungen und die C # Spezifikation macht die Verwendung des Begriffs „zugrunde liegenden Typen“; aber ich weiß nicht, was das bedeutet, noch habe ich je gehört es in Bezug auf etwas anderes als Aufzählungen verwendet. Was bedeutet "zugrunde liegende Typ" eigentlich mittlere


So ist dies noch ein weiterer Fall, dass eine Sonderbehandlung bekommt von der CLR ?

Mein Geld ist auf das der Fall zu sein ... aber eine Antwort / Erklärung wäre schön.


Aktualisieren : Damien_The_Unbeliever Referenz zur Verfügung gestellt, um wirklich diese Frage zu beantworten. Die Erklärung kann in Partition II der CLI-Spezifikation zu finden, im Abschnitt über die Aufzählungen:

  

Für die Zwecke Bindung (zum Beispiel für   eine Methodendefinition von der Lokalisierungs   Verfahren Referenz verwendet zu nennen)   Aufzählungen werden als unterscheidbar sein von ihrer   zugrunde liegende Typ. Für alle anderen   Zwecke, einschließlich der Überprüfung und   Ausführung von Code, ein unboxed Enum   frei Knotens ineinander mit seinem   zugrunde liegende Typ . Aufzählungen können geschachtelt werden   zu einer entsprechenden Instanz geschachtelte   geben, aber diese Art ist nicht die gleiche   als geschachtelte Typ des zugrunde liegenden   geben, so Boxen nicht verlieren nicht die   Originaltyp der Enumeration.

Bearbeiten (wieder?): Warten Sie, tatsächlich, ich weiß nicht, dass ich das Recht vor, das erste Mal gelesen. Vielleicht tut es nicht 100% das spezialisierte Unboxing Verhalten selbst erklären (obwohl ich als angenommen Damien Antwort verlasse, da es sehr viel Licht ins Dunkel bringen). Ich werde auch weiterhin in diese suchen ...


Ein weiteres Bearbeiten : Der Mensch, dann yodaj007 Antwort warf mich für eine andere Schleife Irgendwie eine Enumeration nicht genau das gleiche wie ein int ist;.? noch ein int kann zu einer enum-variablen zugewiesen werden ohne Guss Buh

Ich denke, das ist alles, schließlich beleuchtet von Antwort Hans, weshalb ich es akzeptiert habe. (Sorry, Damien!)

War es hilfreich?

Lösung

Ja, eine besondere Behandlung. Der JIT-Compiler ist sich bewusst, wie Werttypen Arbeit eingerahmt. Was im Allgemeinen ist, was Werttypen schizoiden wirkt ein wenig macht. Boxen umfasst das Erstellen einer System.Object Wert, der genau die gleiche Art und Weise wie ein Wert eines Referenztyps verhält. An diesem Punkt Werte Werttyp nicht mehr verhalten sich wie Werte zur Laufzeit zu tun. Das macht es möglich, zum Beispiel eine virtuelle Methode wie ToString zu haben (). Die Box-Objekt verfügt über eine Methode Tabellenzeiger, wie Referenztypen zu tun.

Der JIT-Compiler kennt die Methodentabellen-Zeiger für Werttypen wie int und bool vorne. Boxing und Unboxing für sie ist sehr effizient, dauert es nur eine Handvoll von Maschinencode-Instruktionen. Dies benötigt eine effiziente zurück in .NET 1.0 sein, um sie wettbewerbsfähig zu machen. A sehr wichtiger Teil davon ist die Einschränkung, dass ein Werttyp Wert nur auf die gleiche Art unboxed sein kann. Dies vermeidet den Jitter von mit einer massiven Switch-Anweisung zu erzeugen, die den korrekten Conversion-Code aufruft. Alles, was es zu tun hat, ist die Methode Tabellenzeiger in dem Objekt zu überprüfen und sicherzustellen, dass es der erwartete Typ ist. Und kopieren Sie den Wert direkt aus dem Objekt heraus. Bemerkenswert ist vielleicht, dass diese Einschränkung nicht in VB.NET nicht existiert, seine CType () Operator in der Tat erzeugen wird Code in eine Hilfefunktion, die diese große switch-Anweisung enthält.

Das Problem mit Enum-Typ ist, dass dies nicht funktionieren kann. Aufzählungen, eine andere GetUnderlyingType () Typ. Mit anderen Worten hat der unboxed Wert so einfach verschiedene Größen den Wert aus dem Box-Objekt kopieren kann nicht funktionieren. Sehr bewusst, wird der Jitter nicht inline der Unboxing Code mehr, erzeugt er einen Anruf an eine Hilfsfunktion in der CLR.

Das Helfer heißt JIT_Unbox () können Sie den Quellcode in der SSCLI20 Quelle, clr / src / vm / jithelpers.cpp finden. Sie werden sehen, es ist speziell mit Aufzählungstypen zu tun. Es ist permissive, erlaubt es Unboxing von einem Aufzählungstyp in einen anderen. Aber nur, wenn der zugrunde liegende Typ gleich ist, erhalten Sie eine InvalidCastException, wenn das nicht der Fall ist.

Welche auch der Grund ist, dass Enum als Klasse deklariert wird. Seine logische Verhalten eines Referenztyps kann abgeleitet Enum-Typen von einem zum anderen geworfen werden. Mit der oben erwähnte Beschränkung der zugrunde liegenden Typs Kompatibilität. Die Werte eines Aufzählungstyp jedoch sehr viel um das Verhalten eines Werttyp Wert. Sie haben Kopie Semantik und Boxen Verhalten.

Andere Tipps

Aufzählungen sind speziell mit der CLR behandelt. Wenn Sie in die blutigen Details gehen wollen, können Sie die MS Partition II spec. Darin werden Sie feststellen, dass Aufzählungen finden:

  

Aufzählungen obey zusätzliche Einschränkungen   als die bereits auf anderen Werttypen.   Aufzählungen muss folgende Angaben enthalten nur Felder   Mitglieder (sie werden nicht einmal definieren   Typ Initialisierungen oder Instanz   Konstruktoren); sie werden nicht   implementieren alle Schnittstellen; Sie   wird auto Feld Layout   (§10.1.2); sie werden genau ein   Beispiel Feld und es wird von dem zugrunde liegenden Typ sein,   die ENUM; alle anderen Felder werden sein   statische und wörtliche (§16.1);

Das ist also, wie sie von System.Enum erben kann, aber ein „Basiswert“ Typ -. Es ist die einzige Instanz Feld sie erlaubt sind zu haben

Es gibt auch eine Diskussion über die Boxen Verhalten, aber es beschreibt nicht ausdrücklich Unboxing auf den zugrunde liegenden Typen, dass ich sehen kann.

Partition I, 8.5.2 besagt, dass Aufzählungen sind „ein alternativer Name für einen vorhandenen Typen“, sondern „[f] oder die Zwecke der passenden Signaturen, eine Enumeration ist nicht das gleiche wie der zugrunde liegende Typ sein.“

Partition II, 14,3 auslegt. „Für alle anderen Zwecke, einschließlich der Überprüfung und Ausführung von Code, ein unboxed Enum frei Knotens ineinander mit dem zugrunde liegenden Typ Aufzählungen kann zu einem entsprechenden Box-Instance-Typ verpackt werden, aber diese Art ist nicht die gleiche als boxed Typ des zugrunde liegenden Typs, so Boxen die ursprüngliche Art der Enumeration nicht verlieren. "

Partition III, 4,32 erläutert das Unboxing Verhalten:. „Die Art von Werttyp enthalten ist innerhalb von obj muss mit kompatibeler Zuordnung sein valuetype [ . Hinweis: Dies bewirkt das Verhalten mit Aufzählungstypen finden Partition II.14.3 Endnote] "

Was ich unter Hinweis darauf, hier ist ab Seite 38 von ECMA-335 (ich schlage vor, Sie laden Sie es einfach, es zu haben):

  

Die CTS unterstützt eine ENUM (auch als ein Aufzählungstyp bekannt ist), einen anderen Namen für einen existierenden Typ. Für die Zwecke der passenden Signaturen wird eine Enumeration nicht das gleiche wie der zugrunde liegenden Typ sein. Instanzen eines ENUM soll jedoch sein zuweisbar zu dem zugrunde liegenden Typ, und vice versa. Das heißt, keine Besetzung (siehe §8.3.3) oder Zwang (siehe §8.3.2) auf convert aus dem Enum auf den zugrunde liegenden Typen erforderlich, noch sind sie verpflichtet, von dem zugrunde liegenden Typ zum ENUM. Eine Enumeration ist wesentlich eingeschränkt als ein echter Typ, wie folgt:

     

Der zugrunde liegende Typ muss ein eingebauter in Integer-Typ sein. Aufzählungen sind von System.Enum ableiten, daher sind sie Werttypen. Wie alle Werttypen, werden sie versiegelt werden (siehe §8.9.9).

enum Foo { Bar = 1 }
Foo x = Foo.Bar;

Diese Aussage wird wegen des zweiten Satzes falsch sein:

x is int

Sie sind die gleiche (ein Alias), aber ihre Unterschrift ist nicht das gleiche. Konvertierung in und aus einem int ist kein gegossen.

Von Seite 46:

  

zugrunde liegenden Typen - in den CTS Aufzählungen sind alternative Namen für bestehende Typen (§8.5.2), bezeichnet als ihre zugrunde liegenden Typ. Mit Ausnahme der Signaturanpassung (§8.5.2) Aufzählungen sind als die ihnen zugrunde liegenden Typ behandelt. Diese Teilmenge ist die Menge der Speichertypen mit den entfernt Aufzählungen.

Gehen Sie zurück zu meinen Foo Enum früher. Diese Anweisung funktioniert:

Foo x = (Foo)5;

Wenn Sie die erzeugte IL-Code meiner Main-Methode in Reflector überprüfen:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
    [0] valuetype ConsoleTesting.Foo x)
L_0000: nop 
L_0001: ldc.i4.5 
L_0002: stloc.0 
L_0003: call string [mscorlib]System.Console::ReadLine()
L_0008: pop 
L_0009: ret 
}

Hinweis gibt es keine Besetzung. ldc ist auf der Seite gefunden 86. Es lädt eine Konstante. i4 ist auf Seite 151 zu finden, die den Typen ist eine 32-Bit-Ganzzahl. Es gibt keine Form!

Extrahiert von MSDN :

  

Der Standard zugrunde liegenden Typ der Aufzählungselemente ist int. Standardmäßig ist der erste Enumerator den Wert 0, und der Wert jedes aufeinanderfolgenden Enumerator wird um 1 erhöht.

So ist die Besetzung möglich, aber Sie müssen es erzwingen:

  

Die zugrunde liegenden Typ gibt an, wie viel Speicherplatz für jeden enumerator zugeordnet ist. Allerdings ist eine explizite Umwandlung benötigt wird, um von Aufzählungstyp zu einem integralen Typ zu konvertieren.

Wenn Sie Ihr Enum in object Feld, das Tier Objekt wird aus System.Enum abgeleitet (der eigentlichen Typ wird zur Laufzeit bekannt), so ist es eigentlich ein int, so die Besetzung gültig ist.

  • (animal is Enum) kehrt true: Aus diesem Grunde Sie Tier in eine Enum oder ein Ereignis in eine int unbox ein explizites Casting tun
  • .
  • (animal is int) kehrt false: Der is Operator (im allgemeinen Typprüfung) nicht den zugrunde liegenden Typ für Aufzählungen überprüfen. Auch aus diesem Grund müssen Sie eine explizite Casting tun Enum zu int konvertieren.

Während Aufzählungstypen von System.Enum vererbt werden, jede Umwandlung zwischen ihnen ist nicht direkt, sondern eine Box / Unboxing ein. Von C # 3.0-Spezifikation:

  

Ein Aufzählungstyp ist ein einzigartiger Typ   mit dem Namen Konstanten. Jeden   Aufzählungstyp hat eine darunter liegende   Typ, das Byte, sbyte sein muss,   Kurz gesagt, ushort, int, uint, long oder   ulong. Die Menge der Werte der   Aufzählungstyp ist das gleiche wie die   Satz von Werten von dem zugrunde liegenden Typ.   Werte des Aufzählungstypen sind nicht   auf die Werte der genannten beschränkt   Konstanten. Aufzählungstypen sind   durch Enumeration definiert   Erklärungen

So, während die Tier Klasse von System.Enum abgeleitet ist, ist es eigentlich ein int. Btw, ist eine andere seltsame Sache ist System.Enum von System.ValueType abgeleitet, aber es ist immer noch ein Referenztyp.

Underlying Typ des Enum ist der Typ verwendet, um den Wert der Konstanten zu speichern. In Ihrem Beispiel, auch wenn Sie nicht explizit die Werte definiert haben, C # tut dies:

enum Animal : int
{
    Dog = 0,
    Cat = 1
}

Intern Animal besteht aus zwei Konstanten mit den ganzzahligen Werten 0 und 1. Deshalb sollten Sie explizit eine ganze Zahl mit einem Animal und einem Animal auf eine ganze Zahl werfen können. Wenn Sie Animal.Dog auf einen Parameter übergeben, die eine Animal akzeptiert, was Sie wirklich tun, ist den 32-Bit-Integer-Wert von Animal.Dog vorbei (in diesem Fall 0) gewonnen. Wenn Sie geben Animal eine neue Basistyp, dann werden die Werte als diese Art gespeichert.

Warum nicht ... es ist durchaus möglich, zum Beispiel für eine Struktur, die eine int intern zu halten, und mit einem expliziten Cast-Operator zu int konvertierbar sein ... kann eine ENUM simulieren:

interface IEnum { }

struct MyEnumS : IEnum
{
    private int inner;

    public static explicit operator int(MyEnumS val)
    {
        return val.inner;
    }

    public static explicit operator MyEnumS(int val)
    {
        MyEnumS result;
        result.inner = val;
        return result;
    }

    public static readonly MyEnumS EnumItem1 = (MyEnumS)0;
    public static readonly MyEnumS EnumItem2 = (MyEnumS)2;
    public static readonly MyEnumS EnumItem3 = (MyEnumS)10;

    public override string ToString()
    {
        return inner == 0 ? "EnumItem1" :
            inner == 2 ? "EnumItem2" :
            inner == 10 ? "EnumItem3" :
            inner.ToString();
    }
}

kann diese Struktur eine Struktur kann ziemlich die gleiche Art und Weise verwendet werden ... natürlich, wenn Sie versuchen, die Art zu reflektieren, und rufen Sie IsEnum Eigenschaft es falsch zurück.

Schauen wir uns einige Nutzungs Vergleich mit dem Äquivalent Enum:

enum MyEnum
{
    EnumItem1 = 0,
    EnumItem2 = 2,
    EnumItem3 = 10,
}

Im Vergleich Verwendungen:

Struct Version:

var val = MyEnum.EnumItem1;
val = (MyEnum)50;
val = 0;
object obj = val;
bool isE = obj is MyEnum;
Enum en = val;

Enum-Version:

var valS = MyEnumS.EnumItem1;
valS = (MyEnumS)50;
//valS = 0; // cannot simulate this
object objS = valS;
bool isS = objS is MyEnumS;
IEnum enS = valS;

Einige Operationen können nicht simuliert werden, aber das alles zeigt, was ich sollte sagen ... Aufzählungen sind besondere, ja ... wie viel aus? nicht sehr viel! =)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top