【问题标题】:How to create a fully lazy singleton for generics如何为泛型创建一个完全惰性的单例
【发布时间】:2012-03-31 21:01:52
【问题描述】:

我的通用单例提供程序有以下代码实现:

public sealed class Singleton<T> where T : class, new()
{
     Singleton()
     {
     }

     public static T Instance
     {
          get { return SingletonCreator.instance; }
     }

     class SingletonCreator
     {
          static SingletonCreator()
          {
          }

          internal static readonly T instance = new T();
     }
}

此示例取自 2 篇文章,我合并了代码以获得我想要的:

http://www.yoda.arachsys.com/csharp/singleton.htmlhttp://www.codeproject.com/Articles/11111/Generic-Singleton-Provider.

这就是我尝试使用上面代码的方式:

public class MyClass
{
     public static IMyInterface Initialize()
     {
          if (Singleton<IMyInterface>.Instance == null  // Error 1
          {
               Singleton<IMyInterface>.Instance = CreateEngineInstance();  // Error 2
               Singleton<IMyInterface>.Instance.Initialize();
          }

          return Singleton<IMyInterface>.Instance;
     }
}

还有界面:

public interface IMyInterface
{
}

Error 1 的错误是:

'MyProject.IMyInterace' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'MyProject.Singleton<T>'

Error 2 的错误是:

Property or indexer 'MyProject.Singleton<MyProject.IMyInterface>.Instance' cannot be assigned to -- it is read only

我该如何解决这个问题,使其符合上述 2 篇文章?任何其他想法或建议表示赞赏。

我的实现是否打破了单例模式?

【问题讨论】:

  • 当然。创建和控制单个对象的生命周期是单例的责任,但您试图为该类的单例类的实例属性分配一个值。从我看到的情况来看,您真正想做的是依赖注入和基于接口的编程。单例仅在您尝试使用稀有资源(例如数据库连接)时才有用,应谨慎使用(甚至避免使用)
  • 我正在尝试为我的 web mvc 应用程序创建一个引擎,它可以处理我需要的一切,依赖注入,并且我只希望该实例存在 1 个实例。
  • 然后接吻。保持愚蠢简单。在你的应用引擎类中编写一个普通的单例,不要为泛型而烦恼。太过分了。
  • @T.Fabre:我猜你说得有道理。如果我的应用程序中需要另一个不同对象的另一个单例实例?

标签: c# asp.net .net c#-4.0 singleton


【解决方案1】:

基本上,你已经在你的单例类上给出了一个类约束,以及 new() 约束。

写的时候

Singleton<IMyInterface>

您使用的接口类型为 T,这违反了您定义的类型约束。

对于错误 2,

Singleton<IMyInterface>.Instance = CreateEngineInstance();

您正在尝试为只读属性赋值。因此,您需要在您的 Instance 属性上定义一个 setter 才能使该行正常工作。

更新

这些方面的东西应该为你做:

public sealed class Singleton
{
     private static Hashtable bindings = new Hashtable();
     private static Hashtable instances = new Hashtable();

     private static void checkType(Type requested, Type bound)
     {
        if (requested.IsValueType)
            throw new Exception("Cannot bind a value type to a reference type");

        // also check type inheritance and other things...
     }

     private static void checkBinding(Type requested)
     {
        if (!(bindings.ContainsKey(requested)))
            throw new Exception(String.Format("Type {0} was not bound !", requested.FullName));
     }

     public static void Bind<T, U>() where U : class, new() 
     {
        checkType(typeof(T), typeof(U));
        bindings[typeof(T)] = typeof(U);
     }

     public static T GetInstance<T>() 
     {
        Type requested = typeof(T);
        Type bound = (Type) bindings[requested];

        checkBinding(requested);

        if (!instances.ContainsKey(requested)) {
            // We know that type "bound" was set with a new() class constraint
            instances[requested] = (T) Activator.CreateInstance(bound);
        }

        return (T) instances[requested];
     }
}

然后你可以写:

 Singleton.Bind<IMyInterface, MyClass>();
 IMyInterface instance = Singleton.GetInstance<IMyInterface>();

如果您想更进一步,您还可以指定此提供程序创建的对象的生命周期,以便您可以使用单例,或者让提供程序为每次调用返回一个新对象,等等。

您还应该看看依赖注入模式,它似乎接近您想要实现的目标,还应该看看已经这样做的现有 DI 框架(NInject、Nhibernate)等等。

【讨论】:

    【解决方案2】:

    当然,你有一个问题。你的通用是假设上课,而不是接口。

    internal static readonly T instance = new T();
    

    您的代码假设要创建该类的实例,您无法实例化接口类型。

    所以,如果你需要某种类型作为单音,你应该写:

    Singleton<MyInterface>.Instance
    

    在哪里

    public class MyInterface : IMyInterface { }
    

    那么您的代码中不需要任何“if”,因为单例负责实例化一个对象并将其保留为一个实例。

    与问题无关:目前 Singletone 被许多开发人员视为“代码气味”,因此通常您必须避免使用它们。试着认为你的应用程序根本没有 Singletone。

    【讨论】:

    • 我有一个我想在我的 mvc 应用程序中使用的引擎。它处理依赖注入、获取当前用户等事情。这就是我想要一个引擎的原因。
    • 考虑到您的评论,我想说 - 您在 MVC 应用程序中做错了什么。如果您在应用程序中有 IoC 容器,其中许多都有 InSingletoneScope() 对象创建策略。您无需创建自己的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2021-02-01
    • 2021-02-24
    相关资源
    最近更新 更多