39 Stimmen

Wenn meine Struktur IDisposable implementiert wird es gepackt, wenn in einer using-Anweisung verwendet werden?

Wenn meine Struktur IDisposable implementiert wird es gepackt, wenn in einer using-Anweisung verwendet werden?

Danke

Bearbeiten: Dieser Timedlock ist eine Struktur und implementiert Idisposable. http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking

Bearbeiten 2: Betrachtet man die IL scheint es, wenn Ihre Struktur Dispose() als öffentliche exponiert, ruft der Compiler Dispose, wenn eine Instanz der Struktur aus dem Bereich geht, wenn Sie vergessen, Dispose() aufrufen (z. B. Sie sind nicht mit der Anweisung "using")?

7 Stimmen

Warum haben Sie eine Struktur, die IDisposable implementiert?

5 Stimmen

Das klingt furchtbar gefährlich. Mit seiner Kopie auf Zuweisung Semantik, ist es sehr, sehr schwer zu sagen, wie viele Kopien einer Struktur Sie haben.

2 Stimmen

@Jonathan Allen - Die Anzahl der Kopien der Struktur ist jedoch im Kontext einer using-Anweisung nicht wirklich wichtig. Wichtig ist, dass die nicht verwaltete Ressource, auf die die Struktur verweist, ordnungsgemäß entsorgt wird. Diese Referenz sollte in der Copy-on-Assignment-Semantik nicht schwer zu verfolgen sein.

30voto

Eric Lippert Punkte 628543

Dies ist ein Duplikat von Wann verpackt eine Verwendungsanweisung ihr Argument, wenn sie eine Struktur ist?

UPDATE: Diese Frage wurde das Thema meines Blogs im März 2011 . Danke für die tolle Frage.

Ein paar Punkte:

  • Wie andere bereits richtig bemerkt haben, wird ein Wertetyp, der IDisposable implementiert, nicht gepackt, wenn er als Folge des Verlassens einer using-Anweisung entsorgt wird.
  • Dies ist technisch gesehen ein Verstoß gegen die C#-Spezifikation. Die Spezifikation besagt, dass der finally-Block die Semantik von ((IDisposable)resource).Dispose(); was eindeutig eine Umstellung auf Boxen ist. Wir generieren die Boxing-Konvertierung nicht wirklich. Da dies in den meisten Fällen das ist, was Sie sowieso wollen, verlieren wir keine schlaflosen Nächte deswegen.
  • Ein Wegwerfwert scheint eine denkbar schlechte Idee zu sein. Es ist zu einfach, versehentlich eine Kopie eines Werttyps zu erstellen; sie werden schließlich nach Wert kopiert.
  • Warum in aller Welt kümmert es Sie, ob diese Boxen funktionieren oder nicht? Ich hoffe, Sie stellen diese Frage nicht, weil Sie möchten, dass die Dispose-Methode die Variable mit dem Werttyp ändert. Das wäre in der Tat eine schlechte Idee. Veränderliche Wertetypen sind böse.

20voto

Samuel Neff Punkte 70231

Nein, es wird nicht verpackt.

using ist kein Methodenaufruf. Es handelt sich um syntaktischen Zucker, den der Compiler einfach in das hier umwandelt:

MyClass m = new MyClass()
try
{
    // ...
}
finally
{
    if (m != null) {
        m.Dispose();
    }
}

Sie verwendet niemals IDisposable in der Deklaration und übergibt die Instanz niemals an etwas anderes. Für eine struct erzeugt der Compiler sogar etwas noch Kleineres:

MyStruct m = new MyStruct()
try
{
    // ...
}
finally
{
    m.Dispose();
}

Da eine Struktur nicht null sein kann.

Um 100 %ig sicher zu sein, dass es keine Boxen gibt, schauen Sie sich die IL an.

Probieren Sie diesen Beispielcode aus:

class StructBox
{
    public static void Test()
    {
        using(MyStruct m = new MyStruct())
        {

        }

        MyStruct m2 = new MyStruct();
        DisposeSomething(m2);
    }

    public static void DisposeSomething(IDisposable disposable)
    {
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    private struct MyStruct : IDisposable
    {           
        public void Dispose()
        {
            // just kidding
        }
    }
}

Dann schauen Sie sich die IL an:

.method public hidebysig static void Test() cil managed
{
    .maxstack 1
    .locals init (
        [0] valuetype ConsoleApplication1.StructBox/MyStruct m,
        [1] valuetype ConsoleApplication1.StructBox/MyStruct m2)
    L_0000: ldloca.s m
    L_0002: initobj ConsoleApplication1.StructBox/MyStruct
    L_0008: leave.s L_0018
    L_000a: ldloca.s m
    L_000c: constrained ConsoleApplication1.StructBox/MyStruct
    L_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0017: endfinally 
    L_0018: ldloca.s m2
    L_001a: initobj ConsoleApplication1.StructBox/MyStruct
    L_0020: ldloc.1 
    L_0021: box ConsoleApplication1.StructBox/MyStruct
    L_0026: call void ConsoleApplication1.StructBox::DisposeSomething(class [mscorlib]System.IDisposable)
    L_002b: ret 
    .try L_0008 to L_000a finally handler L_000a to L_0018
}

Die Zeilen L_0000 bis L_0017 stellen die m Erklärung und using . Es wird nicht geboxt.

Die Zeilen L_0018 bis L_0026 stellen die m2 Erklärung und Aufruf zu DisposeSomething . Siehe Zeile L_0021 box .

9voto

JMarsch Punkte 21084

Dies wird nicht verpackt (was mich überrascht). Ich denke, dass die Erklärung von bnkdev es abdeckt. Hier ist, wie ich es überprüft:

Ich habe die folgende schnelle Konsolenanwendung geschrieben (beachten Sie, dass ich BoxTest() eingeschlossen habe, das ich wissen will box, damit ich etwas zum Vergleichen habe).

Dann habe ich Reflector verwendet, um die kompilierte Ausgabe in IL zu disassemblieren (Sie können ILDASM verwenden).

namespace StructInterfaceBoxingTest
{
    public struct TestStruct : IDisposable
    {
        #region IDisposable Members

        public void Dispose()
        {
            System.Console.WriteLine("Boo!");
        }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (TestStruct str = new TestStruct())
            {

            }
        }

        static void BoxTest()
        {
            TestStruct str = new TestStruct();
            ThisWillBox(str);
        }

        static void ThisWillBox(object item) {}
    }
}

Ok, also zuerst, hier ist die IL für BoxTest - beachten Sie die Box-Anweisung in Zeile L_000a (Sternchen Hervorhebung mein)

.method private hidebysig static void BoxTest() cil managed
{
    .maxstack 1
    .locals init (
        [0] valuetype StructInterfaceBoxingTest.TestStruct str)
    L_0000: nop 
    L_0001: ldloca.s str
    L_0003: initobj StructInterfaceBoxingTest.TestStruct
    L_0009: ldloc.0 
    L_000a: **box** StructInterfaceBoxingTest.TestStruct
    L_000f: call void StructInterfaceBoxingTest.Program::ThisWillBox(object)
    L_0014: nop 
    L_0015: ret 
}

Schauen Sie sich nun Main an (wo wir eine using-Anweisung mit unserer IDisposable-Struktur verwenden):

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 1
    .locals init (
        [0] valuetype StructInterfaceBoxingTest.TestStruct str)
    L_0000: nop 
    L_0001: ldloca.s str
    L_0003: initobj StructInterfaceBoxingTest.TestStruct
    L_0009: nop 
    L_000a: nop 
    L_000b: leave.s L_001c
    L_000d: ldloca.s str
    L_000f: constrained StructInterfaceBoxingTest.TestStruct
    L_0015: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_001a: nop 
    L_001b: endfinally 
    L_001c: nop 
    L_001d: ret 
    .try L_0009 to L_000d finally handler L_000d to L_001c
}

Beachten Sie das Schlüsselwort "constrained" in Zeile L_000f. Ich kann keinen Hinweis darauf finden, was dieses Schlüsselwort genau bedeutet, aber wenn Sie den Beitrag von bnkdev lesen, denke ich, dass dies der eingeschränkte viruelle Aufruf ist, den er beschreibt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X