我有一个使用“静态只读T实例=新T();”的单例图案。但是,我遇到了T是一次性的情况,实际上需要处理以进行单元测试。如何修改此模式以支持一次性单件?
我想要的界面是:
var x = Foo.Instance;
var y = Foo.Instance; // x == y
...
x.Release(); // this causes the next Foo.Instance to return a fresh object
// also, it assumes no further operations on x/y will be performed.
注意 - 当然,模式必须是线程安全的。
编辑 - 出于生产代码的目的,这是一个真正的单身人士。问题在于它锁定了一些文件,因此在单元测试中我们必须处理它。
如果可能的话,我也更喜欢可以重复使用的模式。
答案 0 :(得分:13)
在那一点上,说实话,我认为我不再认为它是单身人士了。
特别是,如果一个客户使用单身人士,他们真的不会指望他们必须处理它,如果其他人这样做,他们会感到惊讶。
您的生产代码要做什么?
编辑:如果你真的,真的需要这个用于单元测试而仅用于单元测试(这在设计方面听起来有问题,坦率而言)那么你总是可以使用反射来调整字段。如果真的是一个单身人士,或者它是否应该真的是一次性的 - 这两个很少一起去,那就更好了。
答案 1 :(得分:12)
将Release
标记为internal
,并使用InternalsVisibleTo
属性将其公开给您的单元测试程序集。您可以这样做,或者如果您对自己的程序集中的某个人发出警告,您可以将其标记为private
并使用反射访问它。
在单例中使用终结器,在单例实例上调用Dispose
方法。
在生产代码中,只有卸载AppDomain
才会导致处理单例。在测试代码中,您可以自己发起对Release
的调用。
答案 2 :(得分:4)
单身人士不应该是一次性的。期。如果有人过早地调用Dispose,那么您的应用程序将被重置,直到它重新启动。
答案 3 :(得分:1)
public class Foo : IDisposable
{ [ThreadStatic] static Foo _instance = null;
private Foo() {IsReleased = false;}
public static Foo Instance
{ get
{ if (_instance == null) _instance = new Foo();
return _instance;
}
}
public void Release()
{ IsReleased = true;
Foo._instance = null;
}
void IDisposable.Dispose() { Release(); }
public bool IsReleased { get; private set;}
}
答案 4 :(得分:0)
您可以使用嵌套的懒惰单例(请参阅here)进行一些简单的修改:
public sealed class Singleton : IDisposable
{
Singleton()
{
}
public static Singleton Instance
{
get
{
if (!Nested.released)
return Nested.instance;
else
throw new ObjectDisposedException();
}
}
public void Dispose()
{
disposed = true;
// Do release stuff here
}
private bool disposed = false;
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
如果已经处理了对象的所有公共方法/属性,请记住抛出ObjectDisposedException。
您还应该为对象提供终结器方法,以防Dispose被调用。了解如何正确实现IDisposable here。
答案 5 :(得分:0)
如果该类实现了IDisposable(正如你所暗示的那样),那么只需调用x.Dispose()
答案 6 :(得分:0)
对于单元测试,您可以使用“手动”实例(但您需要一种实例化对象的方法)。
在你的情况下,你应该更好地使用工厂模式(抽象/方法 - 最适合你的情况),结合单身人士。
如果要测试单例是否已正确处理所使用的对象(在单元测试中),则使用Factory方法,否则使用单例模式。
顺便说一句,如果您无法访问单例源代码或者您不允许修改它,您最好将其包装到另一个单例中,并提供新代码的所有逻辑(更像是代理)。这听起来有点矫枉过正,但它可能是一个可行的解决方案。
此外,为了能够控制对它的访问,提供工厂,并且仅当对象尚未处理时,让客户端获取新对象。
答案 7 :(得分:0)
制作一次性Singleton的另一个选择是为您的类使用SandCastle的 [Singleton] 属性,然后Castle框架负责处理所有一次性Singleton对象