Adapter/Wrapper 和相等的引用
Adapter/Wrapper and equal references
我想为另一种类型创建包装器 class。这可以正常工作,直到(引用)相等的对象需要具有(引用)相等的包装器为止。
一个例子:
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
returns总是同一个实例,只要参数createNew
是false
。
相比之下 TestWrapper.GetInstance
returns 总是一个新实例。
因为我希望能够用 TestWrapper
替换 Test
,所以我搜索了一个解决方案,以便在最后,包装器 returns 只有一个新实例,如果测试returns 一个新实例。但是,TestWrapper 不应该了解 Test 的内部结构。
测试代码为
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())));
}
想要的结果是
True, False
这就是将 new Test()
传递给该方法后得到的结果。
如果您通过 new TestWrapper(new Test())
,您将获得
False, False, False
有一个基于简单缓存的解决方案 (Dictionary<Test, TestWrapper>
) - 但是有了这个,我会在内存中保存许多实例而不进一步使用它们(并且 GC 无法收集这些实例,因为有参考文献)。
我玩了一下 WeakReferences
,但我找不到可以用来存储 WeakReference
的密钥 - 因此我必须遍历缓存列表并搜索正确的实例是缓慢的。此外,我必须为每个成员(有自己的缓存)实现这个解决方案,这似乎不是一个很好的解决方案...
我希望我已经充分解释了我的问题 ;) 所以,我的问题是:
有没有办法作弊object.ReferenceEquals
(这个问题没有收获)
- 我可以使用什么(作为缓存的键)作为对象实例的标识符(这样我就可以使用
WeakReference
)
- 有没有更好的方法来实现真正的适配器(我可以毫不费力地用适配器替换适配器)
我无法访问 Test
class,并且只能有限地访问使用它的代码(只要它实现了接口,我就可以传递任意实例)
不,你不能作弊object.ReferenceEquals()
。但是,object.ReferenceEquals()
被有意使用 很少 ,并且通常在确实需要引用相等的情况下使用。
The runtime need it in order to get things right. E.g. if the instance is used as a key in an Dictionary<>
实际上,运行时通常使用单个对象的 .GetHashCode()
和 .Equals()
行为,但如果您不在 class es,这些方法的基本 System.Object
实现默认依赖于对象引用。
因此,如果您能够更改 Test
class 和 TestWrapper
class 的代码,则可以在那些 classes 以确保他们将等效对象视为相等。
另一种(通常更好的)方法是创建一个 IEqualityComparer<>
实现以用于您的特定用例。您在 Dictionary<>
中提到了键:您可以在创建字典时向字典提供一个 IEqualityComparer<>
实例,以使其完全按照您想要的方式测试是否相等。
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
我想为另一种类型创建包装器 class。这可以正常工作,直到(引用)相等的对象需要具有(引用)相等的包装器为止。
一个例子:
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
returns总是同一个实例,只要参数createNew
是false
。
相比之下 TestWrapper.GetInstance
returns 总是一个新实例。
因为我希望能够用 TestWrapper
替换 Test
,所以我搜索了一个解决方案,以便在最后,包装器 returns 只有一个新实例,如果测试returns 一个新实例。但是,TestWrapper 不应该了解 Test 的内部结构。
测试代码为
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())));
}
想要的结果是
True, False
这就是将 new Test()
传递给该方法后得到的结果。
如果您通过 new TestWrapper(new Test())
,您将获得
False, False, False
有一个基于简单缓存的解决方案 (Dictionary<Test, TestWrapper>
) - 但是有了这个,我会在内存中保存许多实例而不进一步使用它们(并且 GC 无法收集这些实例,因为有参考文献)。
我玩了一下 WeakReferences
,但我找不到可以用来存储 WeakReference
的密钥 - 因此我必须遍历缓存列表并搜索正确的实例是缓慢的。此外,我必须为每个成员(有自己的缓存)实现这个解决方案,这似乎不是一个很好的解决方案...
我希望我已经充分解释了我的问题 ;) 所以,我的问题是:
有没有办法作弊(这个问题没有收获)object.ReferenceEquals
- 我可以使用什么(作为缓存的键)作为对象实例的标识符(这样我就可以使用
WeakReference
) - 有没有更好的方法来实现真正的适配器(我可以毫不费力地用适配器替换适配器)
我无法访问 Test
class,并且只能有限地访问使用它的代码(只要它实现了接口,我就可以传递任意实例)
不,你不能作弊object.ReferenceEquals()
。但是,object.ReferenceEquals()
被有意使用 很少 ,并且通常在确实需要引用相等的情况下使用。
The runtime need it in order to get things right. E.g. if the instance is used as a key in an
Dictionary<>
实际上,运行时通常使用单个对象的 .GetHashCode()
和 .Equals()
行为,但如果您不在 class es,这些方法的基本 System.Object
实现默认依赖于对象引用。
因此,如果您能够更改 Test
class 和 TestWrapper
class 的代码,则可以在那些 classes 以确保他们将等效对象视为相等。
另一种(通常更好的)方法是创建一个 IEqualityComparer<>
实现以用于您的特定用例。您在 Dictionary<>
中提到了键:您可以在创建字典时向字典提供一个 IEqualityComparer<>
实例,以使其完全按照您想要的方式测试是否相等。
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