13

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

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

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

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

4

8 回答 8

15

在这一点上,老实说,我认为我不再认为它是单身人士了。

特别是,如果客户使用单例,他们真的不会期望他们必须处理它,如果其他人这样做,他们会感到惊讶。

你的生产代码要做什么?

编辑:如果你真的,真的需要这个用于单元测试并且用于单元测试(坦率地说,这在设计方面听起来有问题),那么你总是可以使用反射来摆弄这个领域。最好弄清楚它是否真的应该是单例的,或者它是否真的应该是一次性的——这两者很少一起出现。

于 2008-10-22T13:21:44.210 回答
13

标记Releaseinternal并使用该InternalsVisibleTo属性仅将其公开给您的单元测试程序集。您可以这样做,或者如果您担心自己程序集中的某个人会调用它,您可以将其标记为private并使用反射访问它。

在单例中使用终结器来调用Dispose单例实例上的方法。

在生产代码中,只有卸载 anAppDomain才会导致单例的处理。Release在测试代​​码中,您可以向自己发起调用。

于 2008-10-22T14:00:17.953 回答
4

单例不应该是一次性的。时期。如果有人过早地调用 Dispose,那么您的应用程序将被搞砸,直到它重新启动。

于 2008-10-22T13:42:31.017 回答
2
 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;}

  }
于 2008-10-22T13:38:48.963 回答
0

您可以通过一些简单的修改来使用嵌套的惰性单例(参见此处):

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 。

于 2008-10-22T13:36:04.493 回答
0

如果该类实现了 IDisposable (正如您所暗示的那样),那么只需调用 x.Dispose()

于 2008-10-22T13:38:29.293 回答
0

对于单元测试,您可以使用“手动”实例(但您需要一种实例化对象的方法)。

在您的情况下,您可能应该更好地使用工厂模式(抽象/方法 - 以最适合您的情况为准),并结合单例。

如果要测试单例是否正确处理了使用的对象(在单元测试中),则使用工厂方法,否则使用单例模式。

顺便说一句,如果您无权访问单例源代码或不允许修改它,则最好将其包装到另一个单例中,并提供新单例中的所有逻辑(更像是代理)。这听起来有点矫枉过正,但它可能是一个可行的解决方案。

此外,为了能够控制对它的访问,提供一个工厂,并让客户端仅在对象尚未被释放时才获取新对象。

于 2008-10-22T13:39:53.830 回答
0

制作一次性 Singleton 的另一种选择是为您的类使用 SandCastle 的[Singleton]属性,然后 Castle 框架负责处理所有一次性 Singleton 对象

于 2009-04-15T04:04:46.553 回答