Details darüber, was passiert, wenn eine Struktur eine Schnittstelle implementiert
Ich kam vor kurzem in diesem Stackoverflow-Frage: Wann Struktur?
In ihm, er hatte eine Antwort, die sagte etwas, was ein bisschen Tiefe:
Darüber hinaus erkennen, dass, wenn eine Struktur eine Schnittstelle implementiert - als
Enumerator ist - und ist gewirkt, die umgesetzt type, struct
wird ein Referenz-Typ und bewegt wird, um den heap. Interne
Dictionary-Klasse, Enumerator noch einen Wert geben. Doch so schnell
als Methode fordert GetEnumerator(), die einen Verweis-Typ IEnumerator ist
zurückgegeben.
Genau das, was bedeutet das?
Wenn ich etwas wie
struct Foo : IFoo
{
public int Foobar;
}
class Bar
{
public IFoo Biz{get; set;} //assume this is Foo
}
...
var b=new Bar();
var f=b.Biz;
f.Foobar=123; //What would happen here
b.Biz.Foobar=567; //would this overwrite the above, or would it have no effect?
b.Biz=new Foo(); //and here!?
Was genau sind die detaillierte Semantik einen Wert-Typ-Struktur behandelt werden wie ein Referenz-Typ?
- Ich denke, Sie beantwortet diese selbst - "die struct wird ein Referenz-Typ und bewegt wird, um den heap"
- Ich weiß nicht, was Foobar ist in deinem Beispiel...
- also zwei Referenzen auf das gleiche? Was ist, wenn der Wert geben Sie die änderungen? Gibt es eine MSDN-Dokumentation oder etwas zu erklären, das?
- Es wird als boxing bezeichnet und viel Dokumentation auf der MSDN-Website. msdn.microsoft.com/en-us/library/yz2be5wk.aspx Haben Sie getestet, der code, den Sie geschrieben?
- Dies scheint nicht "nur" Boxen. Ich meine, später wird es nicht angezeigt, ein unboxing und die "struct wird ein Referenz-Typ und bewegt wird, um den heap" scheint darauf hinzudeuten, dass der ohne Verpackung Art wird eine veränderbare Box geben
- denken Sie an dieses: wenn ich versuche
Foo foo = b.Biz;
ich bekomme ein 'kann Nicht implizit konvertiert "ifoo") vom Typ Foo` Kompilierungs-Fehler - müssen Sie explizit box/unbox, was, warum und wiefoo = null
da Wert-Typen können nichtnull
(Zeiger auf Wert-Typ auf heap = null) - zwei Verweise immer auf das gleiche Objekt zeigen, das ist sicher. Nur nicht in die Beispiele, die Sie zur Verfügung gestellt. in deinem Beispiel
f.Foobar = 123
Ergebnis wäre ein null-Verweis-Ausnahme-seitb
nicht haben, es istBiz
Feld initialisiert. - Haben Sie den code testen? Es wirft einen Fehler.
- es war einfach getrimmt code. Ich habe es tatsächlich geschafft kompiliert und getestet in meiner Antwort weiter unten. Es gibt einen riesigen Unterschied zwischen dem boxing/unboxing und nur "mit Hilfe" der Schnittstelle
- Nein, das ist nicht beschnitten code aus deiner Antwort, oder Sie würden nicht mal die Frage gepostet. Dass getrimmt code lässt sich der Grund für das scheitern explizit unbox.
- Struct-Mitglieder erhalten
this
eine "byref" [hinter-den-kulissen Begriff für die Sache, die übergeben durch einenref
parameter]. Wenn ein struct-member aufgerufen wird, auf eine Box-Objekt, es verhält sich, als ob der boxed-Objekt hatte ein Feld der Struktur-Typ, und übergeben, das Feld, durchref
wie die Struktur derthis
parameter. Beachten Sie, dass es keine Möglichkeit gibt, nach einer Methode, die auf eine Box-Struktur, um eine Referenz auf das Objekt enthalten. Wenn so eine Methode weitergeben will eine eigene Instanz an eine Methode, die eine Schnittstelle oder ein Verweis geben, es muss re-box.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Jede Deklaration einer Struktur Typ wirklich erklärt zwei Arten innerhalb der Laufzeit: ein Wert geben, und ein heap-Objekt-Typ. Aus der Sicht von externen code, der heap-Objekt-Typ verhält sich wie eine Klasse mit Feldern und Methoden der entsprechende Wert geben. Aus der Sicht der internen code, den heap-Typ verhält sich, als wäre es ein Feld
this
den entsprechenden Wert geben.Versuch warf einen Werttyp in einen Verweistyp (
Object
,ValueType
,Enum
oder jede interface-Typ) erzeugt eine neue Instanz der entsprechenden heap-Objekt-Typ, und geben Sie einen Verweis auf die neue Instanz. Das gleiche passiert, wenn man versucht zu speichern, einen Werttyp in einen Verweistyp-Speicherort, oder übergeben Sie es als Referenz-parameter "Typ". Sobald der Wert hat, wurde in ein heap-Objekt, es verhält sich--aus der Sicht von externen code-als ein heap-Objekt.Die einzige situation, in der Wert geben Sie die Implementierung einer Schnittstelle verwendet werden kann, ohne den Wert geben Sie zuerst in ein heap-Objekt ist, wenn es vergangen, als ein generischer Typ-parameter, die die Art der Schnittstelle als eine Einschränkung. In dieser besonderen situation, interface-Mitglieder können verwendet werden, auf die Wert-Typ Instanz ohne umgewandelt werden zu einem heap-Objekt zuerst.
IDisposable
und dann instanziiert wird innerhalbusing
- Anweisung (wieusing (someclass.GetDisposableStruct()) { ; }
) der compiler ist intelligent genug, um zu emittierenconstrained.<Type>
CIL-Instruktion. Ich denke, das gleiche passiert mitIEnumerable.GetEnumerator()
wenn Sie aufgerufen wird, auf Wert-Typen innerhalbforeach
.null
oder eine Referenz auf ein Objekt auf dem heap. Ein Versuch zu speichern, eine struct in eine solche variable erstellen, die ein Objekt auf dem heap, und speichern eine Referenz auf das Objekt.Lesen über Boxen und unboxing (Suche im internet). Zum Beispiel MSDN: Boxing und Unboxing (C# - Programmierhandbuch).
Siehe auch den thread SO Warum brauchen wir boxing und unboxing in C#?, und die threads mit diesem thread.
Hinweis: Es ist nicht so wichtig, wenn man "konvertieren", um eine Basis Klasse, von den Wert-Typ, wie in
oder "konvertieren", um eine implementierte Schnittstelle, wie in
Nur die Basis-Klassen, die ein
struct
hat, sindSystem.ValueType
undobject
(einschließlichdynamic
). Die Basisklassen einerenum
Art sindSystem.Enum
,System.ValueType
, undobject
.Einer Struktur können beliebige Anzahl von Schnittstellen implementieren (aber es übernimmt keine Schnittstellen von Ihren Basisklassen). Ein enum-Typ implementiert
IComparable
(nicht-generische version),IFormattable
, undIConvertible
da die Basis-KlasseSystem.Enum
implementiert diese drei.IEquatable<T>
existieren für den Zweck, der aufgerufen wird, ohne Boxen.void MyMethod<T>(T t) { ... }
dann natürlicht
ist nicht der boxed, wennT
ist ein Wert-Typ. Und wenn ich hinzufügen, dass eine Einschränkung wiewhere T : IFace
, es wird noch keine boxt
natürlich. Aber wenn, mit der Einschränkung, innerhalb der Methode Körper ich sagent.MemberOfIFace();
welche rechtlichen wegen meiner Einschränkung, wird es dazu führen, boxing an dieser Stelle, denke ich.IFoo
ist Mitgliedint Bar(string)
könnte man schreiben Sie eine statische Methodestatic int CallIFooBar<T>(ref T it, string param) where T:IFoo { return it.Bar(param); }
und verwenden Sie es zum aufrufen von interface-MethodenBar
ohne Boxen, aber das ist eher umständlich.MemberOfIFace
geschrieben wurde explizite interface-Implementierung der Methode aufgerufen werden, ohne die erste "casting" (Boxen) diet
variable TypIFace
? Cool. (Ich bezog mich auf deinen zweiten Kommentar.)Ich bin der Beantwortung Ihrer post über dein experiment auf der 2013-03-04, obwohl ich vielleicht ein bisschen spät 🙂
Beachten Sie dies: Jedes mal, wenn Sie zuweisen eine Struktur, die den Wert einer Variablen von einem interface-Typ (oder schicken Sie es als eine Schnittstelle Typ) es wird geboxt. Denken Sie daran, wie Sie ein neues Objekt (das Feld) werden auf dem heap erstellt, und die Wert der struct wird kopiert es. Dieses Feld wird solange aufbewahrt, bis Sie einen Verweis auf Sie, wie mit jedem Objekt.
Verhalten mit 1, Sie haben die Biz-auto-Eigenschaft vom Typ "ifoo") vom, also, wenn Sie hier einen Wert ein, es wird geschachtelt, und die Eigenschaft wird ein Verweis auf das Feld. Wenn Sie den Wert der Eigenschaft, das Feld wird zurückgegeben. Auf diese Weise, es funktioniert auch meistens, als ob Foo wäre eine Klasse, und Sie bekommen, was Sie erwarten: setzen Sie einen Wert und Sie bekommen es zurück.
Nun, mit Verhalten 2 speichern Sie eine Struktur (Gebiet tmp) und die Biz-Eigenschaft gibt den Wert als eine "ifoo") vom. Das bedeutet, dass jedes mal get_Biz aufgerufen wird, wird ein neues Feld erstellt und zurückgegeben.
Blick durch den Main-Methode: jedes mal, wenn Sie sehen, a b.Biz, das ist ein anderes Objekt (box). Erklären, dass das tatsächliche Verhalten.
E. g. in Zeile
b.Biz gibt ein Feld auf dem heap, setzen Sie den Foobar im es auf 576 und dann, als Sie nicht einen Verweis darauf beibehalten, es ist verloren, sofort für Ihr Programm.
In der nächsten Zeile writeline b.Biz.Foobar, aber dieser Aufruf b.Biz dann wieder eine ganz neue box mit Foobar mit dem Standard-Wert 0, das ist, was gedruckt wird.
Nächsten Zeile die variable f früher war auch gefüllt durch eine b.Biz-Aufruf, erstellt eine neue box, aber Sie hielt eine Referenz für das (f) und setzen Sie dessen Foobar 123, so ist das immer noch das, was Sie in diesem Kasten für den rest der Methode.
So, ich entschied mich, dieses Verhalten zu testen mich. Ich gebe die "Ergebnisse", aber ich kann nicht erklären, warum die Dinge geschehen auf diese Weise. Hoffentlich jemand mit mehr wissen darüber, wie dies funktioniert, können zusammen kommen und erleuchte mich mit eine gründlichere Antwort
Vollständige test-Programm:
Wie Sie sehen können, durch manuelles boxing/unboxing bekommen wir extrem anders Verhalten. Ich nicht ganz verstehen, entweder Verhalten.