2014-09-02 12 views
6

Mam pytanie dotyczące wyrzucania przedmiotów.Jak prawidłowo pozbyć się obiektów: wstrzyknięty w stosunku do posiadanego

rozważyć ten IDisposable klasa

public class MyClass : DisposableParentClass 
{ 
    private MyProp _prop;   

    public MyClass(MyProp prop) 
    { 
     _prop = prop; 
    } 

    public MyClass() 
    {    
     _prop = new MyProp(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      _prop.Dispose(); 
     } 

     base.Dispose(disposing); 
    } 

}  

na pierwszym konstruktorem, MyProp jest wstrzykiwany. Więc MyClass nie jest właścicielem obiektu. Ale na drugim konstruktorze, MyProp jest tworzony lokalnie. Czy zawsze należy wyrzucać MyProp, czy powinienem najpierw sprawdzić, czy jest wstrzykiwany, czy też nie.

public class MyClass : DisposableParentClass 
{ 
    private MyProp _prop;   
    private bool _myPropInjected = false; 

    public MyClass(MyProp prop) 
    { 
     _prop = prop; 
     _myPropInjected = true; 
    } 

    public MyClass() 
    {    
     _prop = new MyProp(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      if (!_myPropInjected) { _prop.Dispose(); } 
     } 

     base.Dispose(disposing); 
    } 

}  
+3

Popieram drugie podejście. Usuwanie przedmiotów, które nie stworzyłeś, może później doprowadzić do wszelkiego rodzaju nieprzyjemnych niespodzianek. – Dirk

+0

Brzmi bardzo podobnie do http://stackoverflow.com/questions/4085939/who-should-call-dispose-on-idisposable-objects-when-passed-into-another-object?rq=1 –

+0

@DmitryBychenko Ponieważ byłoby to być "prawdziwym", gdy zostało ono również wstrzyknięte, potrzebuje czegoś innego (jak ta wartość bool) mówiącego o dwóch sytuacjach. –

Odpowiedz

9

Jeśli klasa powinna obsługiwać te dwie sytuacje:

  1. To nie jest właścicielem podanego obiektu, nie powinien dysponować nim
  2. Jest właścicielem utworzonego obiektu, powinien go pozbyć:

To tak, musisz mieć mechanizm, który odróżnia te dwie sytuacje.

Powszechną metodą (wspólne dla mnie i tak) jest stosowanie konwencji nazewnictwa tak:

private MyProp _prop;   
private bool _ownsProp = false; 

tj. odwróć znaczenie twoich flag, ale to jest detal, twoje rozwiązanie jest w porządku, i tak, musisz mieć takie rozwiązanie.


Jeśli masz mnóstwo tych dziedzinach, w których każdy musi mieć swoje własne pole bool obsłużyć to, być może warto tworzenia klasy pomocnika, takich jak program LINQPad demonstruje:

void Main() 
{ 
    Injectable i1 = new Injectable(); 
    Injectable i2 = new Injectable(new Injected("A")); 
    Injectable i3 = new Injectable(new Injected("A"), new Injected("B")); 

    Debug.WriteLine("dispose a and b"); 
    i1.Dispose(); 

    Debug.WriteLine("dispose b"); 
    i2.Dispose(); 

    Debug.WriteLine("no dispose"); 
    i3.Dispose(); 
} 

public class Injected : IDisposable 
{ 
    public Injected(string name) { Name = name; } 
    public string Name { get; set; } 
    public void Dispose() { Debug.WriteLine(Name + " disposed"); } 
} 

public class Injectable : IDisposable 
{ 
    private Ownable<Injected> _A; 
    private Ownable<Injected> _B; 

    public Injectable(Injected a, Injected b) 
    { 
     _A = Ownable.NotOwned(a); 
     _B = Ownable.NotOwned(b); 
    } 

    public Injectable(Injected a) 
    { 
     _A = Ownable.NotOwned(a); 
     _B = Ownable.Owned(new Injected("B")); 
    } 

    public Injectable() 
    { 
     _A = Ownable.Owned(new Injected("A")); 
     _B = Ownable.Owned(new Injected("B")); 
    } 

    public void Dispose() 
    { 
     _A.Dispose(); 
     _B.Dispose(); 
    } 
} 

public class Ownable<T> : IDisposable 
    where T : class 
{ 
    private readonly T _Instance; 
    private readonly Action _CleanupAction; 

    public Ownable(T instance, bool isOwned) 
    { 
     _Instance = instance; 

     if (isOwned) 
     { 
      IDisposable disposable = instance as IDisposable; 
      if (disposable == null) 
       throw new NotSupportedException("Unable to clean up owned object, does not implement IDisposable"); 

      _CleanupAction =() => disposable.Dispose(); 
     } 
    } 

    public Ownable(T instance, Action cleanupAction) 
    { 
     _Instance = instance; 
     _CleanupAction = cleanupAction; 
    } 

    public T Instance { get { return _Instance; } } 

    public void Dispose() 
    { 
     if (_CleanupAction != null) 
      _CleanupAction(); 
    } 
} 

public static class Ownable 
{ 
    public static Ownable<T> Owned<T>(T instance) 
     where T : class 
    { 
     return new Ownable<T>(instance, true); 
    } 

    public static Ownable<T> Owned<T>(T instance, Action cleanupAction) 
     where T : class 
    { 
     return new Ownable<T>(instance, cleanupAction); 
    } 

    public static Ownable<T> NotOwned<T>(T instance) 
     where T : class 
    { 
     return new Ownable<T>(instance, false); 
    } 
} 
+0

Zauważ, że moja konwencja nazewnictwa używałaby wielkich liter jako pierwszego znaku, ale przyjąłem twoją konwencję obudowy dla tej odpowiedzi, konwencja "OwnsX" jest nadal tym, czego bym użył. –

+0

Czy istnieje mniej szczegółowe rozwiązanie? W mojej sytuacji używam wielu wstrzykniętych właściwości. Specjalnie do testowania – Reynaldi

+0

Ale czy wszystkie będą indywidualnie wstrzykiwane lub posiadane? Czyli, powiedzmy, masz 3 takie obiekty, czy * wszystkie * zostaną wstrzyknięte, czy * wszystkie * zostaną stworzone, czy możesz wstrzyknąć dowolną kombinację obiektów i stworzyć wewnętrznie resztę? Jeśli to drugie, to nie, potrzebujesz jednej takiej wartości na pole, choć oczywiście można ją zawinąć w klasę pomocnika. Jeśli pierwsze typy, musisz rozróżnić między * wszystko * wstrzyknięte lub * wszystko * utworzone, co wymagałoby tylko jednej takiej wartości. –

0

Można tu również wprowadzić inną notatkę.

To zależy od tego, co faktycznie robi twój MyClass.

Na przykład, jeśli mówimy o klasie, która odczytuje strumień wideo z urządzenia, po zastosowaniu do niego niektórych filtrów i zapisaniu danych do pliku określonego przez użytkownika, gdzie zapisywanie pliku odbywa się za pomocą strumienia przekazywanego z zewnątrz, np. to:

public class VideoProcessor : IDisposable { 
    private FileStream _videoFile = null; 
    private VideoProcessor() {} 

    //user specified FileStream 
    public VideoProcessor(FileStream fs) {_videoFile = fs;} 

    public void Dispose() { 
     _videoFile.Dispose(); //Dispose user passed FileStream 
    } 
} 

unieszkodliwianie przekazanego obiektu strumienia podczas wywoływania wywołania, sprawia, że ​​rzeczywiście ma to miejsce.

W innych przypadkach, tak, lepiej nie niszczyć obiektu, jeśli nie jesteś jego właścicielem. Zostaw to dzwoniącemu, aby zdecydować, kiedy nadejdzie odpowiednia pora.

Powiązane problemy