2009-04-27 7 views
1

Ich versuche, ein allgemeines Verfahren zum Anordnen eines Objekts zu erstellen, die IDisposable implementiert, genannt DisposeObject()
Ein IDisposable-Objekt als Referenz übergeben verursacht einen Fehler?

sicherzustellen, dass ich ein Objekt bin Entsorgung von ursprünglichen Referenz darauf, ich versuche, ein Objekt als Verweis zu übergeben.

Aber ich bin immer einen Übersetzungsfehler, die

Der 'ref' Argument Typ Parametertyp stimmt nicht überein

sagt

Im unten (vereinfacht) Code, beide _Baz und _Bar implementieren IDisposable.

alt text

So sind die Fragen,

  1. Warum bin ich diesen Fehler?
  2. Gibt es einen Weg, um es zu umgehen?

[UPDATE] Von gibt Antworten so weit, solange ich null kein IDisposable Argument auf, ich kann einfach ein Objekt nach Wert übergeben, ohne ref zu verwenden. Ich habe jetzt ein weiteres Problem, ob Einwegobjekte zu null oder nicht innerhalb DisposeObject Methode festgelegt werden. Hier

ist die vollständige Quelle der Vollständigkeit halber:

public class Foo : IDisposable 
{ 
    private Bar _Bar; 
    private Baz _Baz; 
    private bool _IsDisposed; 

    ~Foo() { Dispose(false); } 

    public void Dispose(bool disposing) 
    { 
     if (!_IsDisposed) 
     { 
      if (disposing) 
      { 
       DisposeObject(ref _Baz); 
       DisposeObject(ref _Bar); 
      } 
     } 

     _IsDisposed = true; 
    } 

    private void DisposeObject(ref IDisposable obj) 
    { 
     try 
     { 
      if (obj == null) 
       return; 
      obj.Dispose(); 
      obj = null; 
     } catch (ObjectDisposedException) { /* Already Disposed... */ } 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

public class Bar : IDisposable 
{ 
    public void Dispose() {} 
} 

public class Baz : IDisposable 
{ 
    public void Dispose() {} 
} 

[RESULT]
ich den Code entfernt, das Argument setzt auf null (obj = null;) innerhalb DisposeObject So ist der endgültige Code wurde.

public void Dispose(bool disposing) 
    { 
     if (!_IsDisposed) 
     { 
      if (disposing) 
      { 
       DisposeObject(_Baz); 
       DisposeObject(_Bar); 
      } 
     } 

     _IsDisposed = true; 
    } 

    private void DisposeObject(IDisposable obj) 
    { 
     try 
     { 
      if (obj == null) 
       return; 
      obj.Dispose(); 
     } catch (ObjectDisposedException) { /* Already Disposed... */ } 
    } 
+1

Ich sehe keinen besonderen Grund für hier Referenzparameter. Könnten Sie vielleicht näher ausführen? – Noldorin

+0

@Noldorin: Meine ursprüngliche Implementierung bestand darin, ein Argument zu disponieren und auf null zu setzen. "obj == null;" Das war der Grund für "Ref". – Sung

+1

obj == null ist ein Vergleich, keine Zuordnung. obj = null ist eine Zuweisung. –

Antwort

4

Es ist nicht erforderlich, dass Sie als Referenz übergeben werden, da Sie einen Referenztyp übergeben. Sie sollten das Schlüsselwort ref aus Ihrer Methodendefinition entfernen. Tun Sie dies, und Sie sollten keine Probleme haben, obwohl ich nicht sicher bin, wie dies effektiver oder klarer ist als einfach Dispose() (abgesehen von der Tatsache, dass Sie es nicht für explizite Implementierungen zu werfen haben und dies tut eine null check für dich).

bearbeiten

Tanz, während ich die Diskussion hoffen, dass dieses Thema hilfreich war für Sie, Ihre ursprüngliche Absicht umgeben ist scheint nicht etwas zu sein, das machbar ist. Um etwas wie ref weiterzuleiten, können Sie keine Variable übergeben, die einen anderen als den vom Parameter ref erwarteten Typ aufweist (mit anderen Worten, Sie können keine Variable übergeben, die als class oder interface deklariert wurde, die IDisposable implementiert, wenn ref Parameter ist IDisposable). Da die Parameter ref ermöglichen, dass Zuweisungen an den Aufrufer weitergegeben werden, würden Sie die Möglichkeit eröffnen, dass inkompatible Typen in Ihrer Variablen gespeichert werden.

Ihre beste Wette hier ist, null selbst zuzuweisen, wenn das ist, was Sie wollen. Wenn Sie die null Ausnahmen einchecken und ignorieren möchten in die Funktion, die in Ordnung ist, aber ref wird für Sie in diesem Szenario nicht funktionieren, egal wie Sie es schneiden, leider.

+0

Ich wollte "NULL" und "ObjectDisposedException" an eine Methode delegieren, anstatt "try..catch" für jedes Objekt schreiben zu müssen. – Sung

+0

Ich stimme dir zu, Adam. Ich nehme an, dass er es so macht, dass die Objektreferenz annulliert wird, so dass, wenn DisposeObject mit der gleichen Referenz erneut aufgerufen wird, es früh beendet wird. –

+2

Laut Microsoft-Richtlinien sollte der Aufruf von Dispose() niemals zu einer ObjectDisposedException führen (oder generelle, wiederholte Aufrufe von Dispose() sollten niemals einen Fehler erzeugen). Trotzdem ist das Keyword ref nicht erforderlich. Es fügt nur Komplexität und größere Barrierefreiheitsanforderungen hinzu. Sie sollten es entfernen. –

4

Versuchen Sie folgendes:

IDisposable d = (IDisposable)_Baz; 
DisposeObject(ref d); 

bearbeiten: Als Adam weist darauf hin, Ihren Code nicht dies erfordern ref zu sein. Objekte werden immer als Referenzen übergeben.

+0

Das funktioniert eigentlich nicht. Sie erhalten einen Fehler, dass "das Argument ref oder out eine zuweisbare Variable sein muss". – Noldorin

+0

Bearbeitet nach Noldorins Kommentar. –

+0

Ja, das sollte jetzt den Trick machen. Obwohl ich immer noch nicht denke, dass es einen Grund für das OP gibt, Ref-Parameter hier zu verwenden. – Noldorin

2

Dieser Ansatz riecht komisch, aber ich werde das jetzt ignorieren.

Ihr Problem zu beheben, müssen Sie die Objekte werfen Sie mit „(IDisposable)“

ich dem Willen des Compilers verursacht, und Jon Skeet sind vorbei. Sie benötigen ein tatsächliches Objekt hierfür:

IDisposable _BazD = (IDisposable)_Baz; 
DisposeObject(ref _BazD); 

Ich würde auch eine Null-Check-in Ihren DisposeObject() zusätzlich zu dem try/catch hinzufügen.Das "obj == null" wird eine schnelle und einfache Überprüfung sein, wenn es mit teurem Ausnahmefangen verglichen wird, sollte dieses mehrmals für das gleiche Objekt getroffen werden. Hmm ... war das vor einer Minute? Vergiss es.

+0

Warum müsste ich das Objekt explizit als "IDisposable" darstellen? Kann der Compiler daraus schließen, dass ich tatsächlich ein Objekt übergebe, das IDisposable implementiert? – Sung

+0

Sicher könnte es, aber es tut es nicht. –

+0

@Michael: Das Schlüsselwort ref ist das, was das einschränkt. Wäre das ref-Schlüsselwort nicht da, würde es genau das tun. –

5

Hier ist eine Option für Ihr Beispiel (es kann gegen einen Compiler jetzt nicht überprüfen, aber Sie bekommen die Idee):

private void DisposeObject<T>(ref T obj) where T : IDisposable 
{ 
    // same implementation 
} 

es zu nennen, verwenden

DisposeObject<Baz>(ref _Baz); 
DisposeObject<Bar>(ref _Bar); 

Wie in den anderen Kommentaren erwähnt, hat der Compilerfehler, den Sie erhalten, seinen eigenen Zweck (Sie können nicht einen anderen Typ von IDisposable innerhalb Ihrer Methode zuweisen, was zu einem inkonsistenten Zustand führt).

+0

Das ist schick. Es kompiliert, aber da ich mich entschieden habe, kein Argument auf null zu setzen, habe ich bereits eine Antwort ausgewählt. Ich denke, dass ich den obigen Code für zukünftige Anwendungen verwenden kann. Danke Dan C. – Sung

+0

Sehr schlau, und die meiste Zeit können Sie sich auf Typreferenz verlassen (z.B. 'DisposeObject (ref _Baz) ',' DisposeObject (ref _Bar) ') –

0

Danke Dan C. Ich habe noch nicht genügend Rep für Kommentare, also muss ich das als Antwort hinzufügen. Allerdings volle Anerkennung für Dan C für diese Lösung.

Dies funktioniert Code:

public override void Dispose() 
{ 
    base.Dispose(); 

    DisposeOf<UserTableAdapter>(ref userAdapter); 
    DisposeOf<ProductsTableAdapter>(ref productsAdapter); 

    if (connection != null) 
    { 
     if (connection.State == ConnectionState.Open) 
     { 
      connection.Close(); 
     } 
     DisposeOf<SqlConnection>(ref connection); 
    } 
} 

private void DisposeOf<T>(ref T objectToDispose) where T : IDisposable 
{ 
    if (objectToDispose != null) 
    { 
     objectToDispose.Dispose(); 
     objectToDispose = default(T); 
    } 
} 
Verwandte Themen