2016-05-05 5 views
0

Ich möchte eine Wrapper-Klasse für einen anderen Typ erstellen. Dies funktioniert bis zu dem Punkt, wo es notwendig ist, dass (Referenz) -gleiche Objekte (Referenz) -gleiche Wrapper haben müssen.Adapter/Wrapper und gleiche Referenzen

Ein Beispiel:

public interface ITest<T> 
{ 
    T GetInstance(bool createNew); 
} 

public class Test : ITest<Test> 
{ 
    private static Test instance; 

    public Test GetInstance(bool createNew) 
    { 
     if (instance == null || createNew) 
     { 
      instance = new Test(); 
     } 
     return instance; 
    } 
} 


public class TestWrapper : ITest<TestWrapper> 
{ 
    private readonly Test wrapped; 
    public TestWrapper(Test wrapped) 
    { 
     this.wrapped = wrapped; 
    } 

    public TestWrapper GetInstance(bool createNew) 
    { 
     return new TestWrapper(wrapped.GetInstance(createNew)); 
    } 
} 

Test.GetInstance kehrt immer die gleiche Instanz, solange der Parameter createNew ist false. Im Gegensatz dazu gibt TestWrapper.GetInstance immer eine neue Instanz zurück.

Da ich Test durch TestWrapper ersetzen kann, suche ich nach einer Lösung, so dass der Wrapper am Ende nur eine neue Instanz zurückgibt, wenn Test eine neue Instanz zurückgibt. Der TestWrapper sollte jedoch keine Kenntnisse über die Interna von Test haben.

Der Testcode ist

private static void RunTest<T>(ITest<T> cls) 
{ 
    var i1 = (ITest<T>)cls.GetInstance(false); 
    var i2 = (ITest<T>)cls.GetInstance(false); 
    var i3 = (ITest<T>)cls.GetInstance(true); 

    var dic = new Dictionary<ITest<T>, bool>(); 
    if (!dic.ContainsKey(i1)) dic.Add(i1, false); else dic[i1] = true; 
    if (!dic.ContainsKey(i2)) dic.Add(i2, false); else dic[i2] = true; 
    if (!dic.ContainsKey(i3)) dic.Add(i3, false); else dic[i3] = true; 

    Console.WriteLine(string.Join(", ", dic.Select(a => a.Value.ToString()))); 
} 

Das gewünschte Ergebnis ist

True, False 

und das ist, was man bekommt, wenn man new Test() dieses Verfahren durchläuft. Wenn Sie new TestWrapper(new Test()) passieren, werden Sie

False, False, False 

erhalten eine Lösung ist auf einem einfachen Cache basiert (Dictionary<Test, TestWrapper>) - aber damit würde ich viele der Instanzen im Speicher halten, ohne sie jede weitere Verwendung (und der GC konnte diese Instanzen nicht sammeln, da es eine Referenz gibt, die sie enthält).

spielte ich mit WeakReferences ein bisschen herum, aber ich kann nicht einen Schlüssel erkennen, dass ich die WeakReference speichern können - also muss ich durch die Cache-Liste durchlaufen und für die richtige Instanz suchen, die langsam ist. Außerdem muss ich diese Lösung für jedes Mitglied implementieren (mit seinem eigenen Cache), was keine gute Lösung zu sein scheint ...

Ich hoffe, ich habe mein Problem ausreichend erklärt;) Meine Fragen sind also:

  • ist es eine Möglichkeit, object.ReferenceEquals zu betrügen (diese Frage ist nicht lohnend)
  • was kann ich (als Schlüssel für den Cache) als Kennung für eine Objektinstanz verwenden (so kann ich WeakReference verwenden)
  • gibt es einen besseren Weg, um einen echten Adapter (wo ich kann ersetzen die adaptierten mit einem Adapter ohne Kopfschmerzen)

Ich habe keinen Zugriff auf die Test Klasse und nur begrenzten Zugriff auf den Code, der es verwendet (ich bin in der Lage, so lange eine beliebige Instanz übergeben es implementiert ist die Schnittstelle)

+3

Sie können nicht betrügen 'object.ReferenceEquals()', aber das ist der Grund, warum 'object.Equals()' virtuell ist und warum wir haben Dinge wie 'IEqualityComparer'. * Warum * brauchst du 'ReferenceEquals()', um hier wahr zurückzugeben? – StriplingWarrior

+0

Haben Sie versucht, '==' operator zu überschreiben: 'public static bool operator == (T1 obj1, T2 obj2)'. – MaKCbIMKo

+0

@StriplingWarrior: ** Ich ** brauche ReferenceEquals nicht, um wahr zu sein;). Die Laufzeit braucht es, um die Dinge richtig zu machen. Zum Beispiel wenn die Instanz als Schlüssel in einem Dictionary <> verwendet wird. –

Antwort

1

Nein, Sie können nicht schummeln object.ReferenceEquals(). Allerdings wird object.ReferenceEquals() absichtlich sehr selten verwendet, und in der Regel in Fällen, in denen die Dinge wirklich Referenz-gleich sein müssen.

Die Laufzeit muss es, um die Dinge richtig zu machen. Z.B. wenn die Instanz als Schlüssel in einem Dictionary<>

tatsächlich verwendet wird, verwendet die Laufzeit typischerweise das .GetHashCode() und .Equals() Verhalten der einzelnen Objekte, aber es passiert einfach so, dass, wenn Sie nicht, dass das Verhalten in Ihren Klassen außer Kraft setzen , die Basis System.Object Implementierung dieser Methoden beruht auf der Objektreferenz standardmäßig.

Also, wenn Sie die Möglichkeit haben, für den Code zu ändern sowohl die Test Klasse und die TestWrapper Klasse, können Sie diese Gleichheit Methoden in diesen Klassen außer Kraft setzen, um sicherzustellen, dass sie gleichwertige Objekte als gleich erkennen.

Ein alternativer (und normalerweise besserer) Ansatz wäre die Erstellung einer IEqualityComparer<> Implementierung, die in Ihrem spezifischen Anwendungsfall verwendet werden kann. Sie haben Schlüssel in einem Dictionary<> erwähnt: Sie können eine IEqualityComparer<>-Instanz für das Wörterbuch bereitstellen, wenn es erstellt wird, damit es genau auf die von Ihnen gewünschte Weise auf Gleichheit getestet wird.

var dict = new Dictionary<object, object(new TestsAndWrappersAreEqualComparer()); 
var test = Test.GetInstance(true); 
var testWrapper = TestWrapper.GetInstance(true); 
dict[test] = test; 
Console.WriteLine(dict.ContainsKey(test)); // true 
+0

Entschuldigung, meine Schuld. 'GetHashCode' und' Equals' sind ausreichend für 'ContainsKey' genau wie Sie es erklärt haben (und die Overrides werden aufgerufen). Danke! Also am Ende ist es nicht das Gleiche, aber ähnlich genug;) –