2013-02-13 10 views
8

Rozważmy następujący kod:Jak zdobyć cel słabego odniesienia w sposób bezpieczny

var weakRef = new WeakReference(new StringBuilder("Mehran")); 
if (weakRef.IsAlive) 
{ 
    // Garbage Collection might happen. 
    Console.WriteLine((weakRef.Target as StringBuilder).ToString()); 
} 

To możliwe GC.Collect uruchomić po sprawdzeniu weakRef.IsAlive i przed użyciem weakRef.Target.

Czy jestem w tym zły? Jeśli to możliwe, czy istnieje bezpieczny sposób, aby to zrobić?

Na przykład odpowiedni byłby interfejs API taki jak weakRef.GetTargetIfIsAlive().

+1

Zapoznaj się z http://msdn.microsoft.com/en-gb/library/ms404247.aspx –

+1

Najpierw należy przesłać silne odwołanie, a następnie sprawdzić wartość "null". W żaden sposób nie możesz zagwarantować, że twoje silne referencje nie będą wcześniej puste. –

Odpowiedz

12

To API już istnieje; weakRef.Target zwraca null, jeśli obiekt został już zebrany śmieci.

StringBuilder sb = weakRef.Target as StringBuilder; 
if (sb != null) 
{ 
    Console.WriteLine(sb.ToString()); 
} 
1

Weź lokalną kopię celu i sprawdź, czy jest pusta.

WeakReference.Target zwróci null, jeśli cel został zebrany, ale obawiasz się, że został on pobrany między Twoją próbą a uzyskaniem celu.

var weakRef = new WeakReference(new StringBuilder("Mehran")); 

if (weakRef.IsAlive) 
{ 
    var stringBuilder = weakRef.Target as StringBuilder; 

    if (stringBuilder != null) 
    { 
     Console.WriteLine(stringBuilder.ToString()); 
    } 
} 

Console.WriteLine((weakRef.Target as StringBuilder).ToString()); rzuci wyjątek odwołania zerowego, jeśli rzutowanie nie powiedzie się.

+0

Zastanawiam się, dlaczego sprawdzanie 'weakRef.IsAlive' jest wymagane, @supercat odpowiedział na pytanie, więc myślę, że sprawdzanie nie jest konieczne. – mehrandvd

+1

@mehrandvd Może nie być konieczne, użyłem go tylko w tym przykładzie, ponieważ było oparte na pierwotnym pytaniu. Nie może zaszkodzić jej użyciu, ale jest to prawdopodobnie bezsensowne sprawdzenie, biorąc pod uwagę opcję 'as' cast i null check' .Target'. –

+1

@ TrevorPilley: Używanie 'IsAlive' na' WeakReference', jeśli wartość 'true' oznaczałaby, że chcemy odzyskać cel, jest bezcelowa. Możliwe, że z niektórymi garbage collectorami używasz 'if (wr1.IsAlive && wr2.IsAlive && wr3.IsAlive) {Foo wt1 = wr1.Target jako Foo, wt2 = wr2.Target jako Foo, wt3 = wr3.Target jako Foo; if (wr1! = null && wr2! = null && wr3! null) {... użyj wszystkich trzech rzeczy ...} 'może być pomocne, np. współbieżny garbage collector pracował, śledząc, czy od czasu GC zostało utworzone odniesienie do czegoś. Na takim GC, jeśli wr3 zginął, ale wr1 i wr2 żył ... – supercat

9

Właściwość IsAlive nie istnieje dla korzyści kodu, który będzie chciał użyć celu, jeśli jest on żywy, ale raczej dla korzyści kodu, który chce się dowiedzieć, czy cel padł, ale nie byłby zainteresowany dostęp do niego w każdym przypadku. Jeśli kod miałby przetestować Target przed wartością NULL, to spowoduje to, że Target będzie chwilowo mieć silnie zakorzenione odwołanie (kod sprawdzający pod względem wartości NULL) i możliwe, że akt generowania takiego zrootowanego odniesienia może uniemożliwić zbieranie się z obiektu kiedy byłoby inaczej. Jeśli kod nie jest zainteresowany Target, z wyjątkiem tego, aby dowiedzieć się, czy zostało ono jeszcze unieważnione, nie ma powodu, aby kod uzyskiwał odwołanie. Może po prostu przetestować IsAlive i podjąć odpowiednie działanie, jeśli zwróci false.

+0

Zastanawiam się, dlaczego istnieje "IsAlive". To bardzo sprytna odpowiedź, dzięki. – mehrandvd

+1

@mehrandvd: Nietrudno wyobrazić sobie współbieżną GC, w której akt pobrania "celu" z "WeakReference" uniemożliwiłby zbieranie tego obiektu w następnym cyklu *, nawet jeśli odniesienie zostało natychmiast odrzucone *.W takim systemie kod, który chciał podjąć pewne działanie, gdy tylko obiekt nie był już potrzebny i użył "Target" zamiast "IsAlive", mógłby w końcu utrzymać przedmiot na zawsze ożywiony. – supercat

Powiązane problemy