2017-03-02 2 views
2

Ich verwende die folgende Methode, um nach einem Schlüssel in der Registrierung zu suchen und seinen Wert zurückzugeben. Ich übergebe einen Stammschlüssel, um von und den relativen Pfad des Ziels als eine Zeichenfolge zu suchen. Während ich den Pfad analysiere, benutze ich den vorherigen Schlüssel als Basis, um die nächste Ebene zu öffnen. Ich bin streng read-only also, ist es notwendig, den Basisschlüssel zu schließen, nachdem ich damit fertig bin? Und wenn ja, ist das der richtige Weg?Wie kann man RegistryKey ordnungsgemäß schließen, wenn man es verkettet, um nach dem Schlüssel zu suchen?

public static string QueryRegistry (RegistryKey root, string path) 
{ 
    return path.Split(Path.DirectorySeparatorChar) 
    .Aggregate(root, (r, k) => 
    { 
     var key = r?.OpenSubKey(k); 
     r?.Close(); 
     return key; 
    }).GetValue(null).ToString(); 
} 
+0

'RegistryKey' ist' IDisposable': http://Stackoverflow.com/a/9905046/1132334. [this] (https://msdn.microsoft.com/en-us/library/microsoft.win32.registrykey (v = vs.90) .aspx) schlägt vor, dass es ausreicht, den obersten Schlüssel in 'using' zu legen. – dlatikay

+0

@dlatikay, wo es darauf hindeutet, dass es ausreicht, nur Root-Schlüssel zu entsorgen? – Evk

+0

Ok, ja, ich wusste, dass es "IDisposable" war, aber ich war mir nicht sicher, wie man eine using-Anweisung auf Funktionsaufrufe anwenden konnte. Warum nur der oberste Schlüssel? –

Antwort

1

Also Ihr grundsätzliches Problem hier ist, dass Sie eine unbestimmte Anzahl von IDisposable Objekte erstellen, und Sie sind nicht sicher, wenn Sie bereit sein, sie zu schließen. In einer solchen Situation erstelle ich eine Sammlung, um sie zu verfolgen und dann alles in der Sammlung zu entsorgen, wenn ich fertig bin. Hier besteht die Gefahr, dass Sie Ihre Objekte nicht ordnungsgemäß entsorgen.

public static string QueryRegistry (RegistryKey root, string path) 
{ 
    List<IDisposable> resourceTracker = new List<IDisposable>() { root }; 
    string ret = null; 
    try 
    { 
     ret = path.Split(Path.DirectorySeparatorChar) 
     .Aggregate(root, (r, k) => 
     { 
      var key = r?.OpenSubKey(k); 
      if (key != null) 
      { 
       resourceTracker.Add(key); 
      } 
      return key; 
     }).GetValue(null).ToString(); 
    } 
    finally 
    { 
     foreach (var res in resourceTracker) 
     { 
      res.Dispose(); 
     } 
    } 
    return ret; 
} 

Sie könnten versuchen, dies in einer CER zu tun, aber ich bin mir ziemlich sicher, öffnen die neuen Schlüssel als Zuteilungen zählen, die die CLR bedeuten würde es nicht behandeln als CER sowieso. Aber das sollte wahrscheinlich sicher genug sein.

Dies könnte in einer Sammlung abstrahiert werden (so etwas wie rory.ap vorgeschlagen) wie folgt:

public class DisposableCollection : IList<IDisposable>, IDisposable 
{ 
    private List<IDisposable> disposables = new List<IDisposable>(); 

    #region IList<IDisposable> support 

    public int Count 
    { 
     get 
     { 
      return ((IList<IDisposable>)disposables).Count; 
     } 
    } 

    public bool IsReadOnly 
    { 
     get 
     { 
      return ((IList<IDisposable>)disposables).IsReadOnly; 
     } 
    } 

    public int IndexOf(IDisposable item) 
    { 
     return ((IList<IDisposable>)disposables).IndexOf(item); 
    } 

    public void Insert(int index, IDisposable item) 
    { 
     ((IList<IDisposable>)disposables).Insert(index, item); 
    } 

    public void RemoveAt(int index) 
    { 
     ((IList<IDisposable>)disposables).RemoveAt(index); 
    } 

    public void Add(IDisposable item) 
    { 
     ((IList<IDisposable>)disposables).Add(item); 
    } 

    public void Clear() 
    { 
     ((IList<IDisposable>)disposables).Clear(); 
    } 

    public bool Contains(IDisposable item) 
    { 
     return ((IList<IDisposable>)disposables).Contains(item); 
    } 

    public void CopyTo(IDisposable[] array, int arrayIndex) 
    { 
     ((IList<IDisposable>)disposables).CopyTo(array, arrayIndex); 
    } 

    public bool Remove(IDisposable item) 
    { 
     return ((IList<IDisposable>)disposables).Remove(item); 
    } 

    public IEnumerator<IDisposable> GetEnumerator() 
    { 
     return ((IList<IDisposable>)disposables).GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return ((IList<IDisposable>)disposables).GetEnumerator(); 
    } 

    public void AddRange(IEnumerable<IDisposable> range) 
    { 
     disposables.AddRange(range); 
    } 

    public IDisposable this[int index] 
    { 
     get 
     { 
      return ((IList<IDisposable>)disposables)[index]; 
     } 

     set 
     { 
      ((IList<IDisposable>)disposables)[index] = value; 
     } 
    } 
    #endregion 

    #region IDisposable Support 
    private bool disposedValue = false; // To detect redundant calls 


    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposedValue) 
     { 
      if (disposing) 
      { 
       foreach(var disposable in disposables) 
       { 
        disposable.Dispose(); 
       } 
      } 

      // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. 
      // TODO: set large fields to null. 

      disposedValue = true; 
     } 
    } 

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. 
    // ~DisposableCollection() { 
    // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 
    // Dispose(false); 
    // } 

    // This code added to correctly implement the disposable pattern. 
    public void Dispose() 
    { 
     // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 
     Dispose(true); 
     // TODO: uncomment the following line if the finalizer is overridden above. 
     // GC.SuppressFinalize(this); 
    } 


    #endregion 
} 

die dann wie verwendet werden könnten:

public static string QueryRegistry (RegistryKey root, string path) 
{ 
    string ret = null; 
    using (var resourceTracker = new DisposableCollection() { root }) 
    { 
     ret = path.Split(Path.DirectorySeparatorChar) 
     .Aggregate(root, (r, k) => 
     { 
      var key = r?.OpenSubKey(k); 
      if (key != null) 
      { 
       resourceTracker.Add(key); 
      } 
      return key; 
     }).GetValue(null).ToString(); 
    } 
    return ret; 
} 

Diese Klasse ein wenig verlängert werden könnte auch in konkurrierenden Situationen zu arbeiten (sagen wir mit ConcurrentBag oder ConcurrentQueue), und könnte für andere spezifische Bedürfnisse (wie eine Sammlung von RegistryKey s) erweitert werden, und wäre nützlich, wenn Sie diese Logik an mehreren Orten benötigen.

+1

Sie können auch eine Einweg-Sammlung der Schlüssel erstellen und dann einfach einen 'using'-Block für die Sammlung verwenden. Die "Dispose" -Methode der Sammlung würde diese durchlaufen und beseitigen. Könnte robuster sein, aber ja ein bisschen mehr Arbeit. Es ist jedoch ein wiederverwendbares Muster, so dass es sich lohnt. –

+0

Gute Idee - Ich habe bearbeitet, um ein Beispiel zu geben, die Sammlung zumindest generischer zu abstrahieren. Ich bin mir nicht sicher, wieviel Wert hinzugefügt werden würde, indem ich dies speziell für 'RegistryKey' und nicht für' IDisposable' tue, da die Sammlung wirklich nur den Aufruf von 'Dispose()' interessiert, aber das hängt von anderen Anwendungsfällen ab die App nehme ich an. –

+0

Hallo @ DanField, das ist eine brillante Lösung für den Fall eines unbestimmten Lebenszyklus. In meinem Fall weiß ich, dass der Schlüssel nicht mehr benötigt wird, nachdem ich ihn benutzt habe.OpenSubKey' Methode, um die nächste Ebene zu erhalten. Das bedeutet, ich weiß, wann ich sie schließen muss. Gibt es da ein Problem mit meiner Methode? Gibt es einen Vorteil bei der Verpackung in einer Gebrauchsanweisung oder bei der Entsorgung im Vergleich zur Einzelentsorgung? –

Verwandte Themen