C#中的一次性单例

时间:2008-10-22 13:19:27

标签: c# .net singleton dispose

我有一个使用“静态只读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.

注意 - 当然,模式必须是线程安全的。

编辑 - 出于生产代码的目的,这是一个真正的单身人士。问题在于它锁定了一些文件,因此在单元测试中我们必须处理它。

如果可能的话,我也更喜欢可以重复使用的模式。

8 个答案:

答案 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对象