2012-03-31 49 views
1

完全懒惰的单身,我有以下代码实现我一般单供应商:如何创建泛型

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.html and http://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 

我该如何解决这个问题,以便它符合上面提到的两篇文章?任何其他想法或建议表示赞赏。

我的实现是否打破Singleton模式?

+0

单身已死; [生命期/范围应该由依赖注入容器来处理](http://stackoverflow.com/questions/4484619/does-mef-lend-any-value-to-the-singleton-pattern/4484889#4484889)。 – 2012-03-31 12:18:27

+0

当然。创建和控制单个对象的生命周期是单身人士的责任,但是您正在尝试为该类别外的单身人员类的实例属性赋值。从我看到的依赖注入和基于接口的编程中,你真正想要做什么。 Singleton只在尝试使用sparce资源(例如数据库连接)时非常有用,并且应谨慎使用(甚至可以避免) – 2012-03-31 12:22:12

+0

我正在尝试为我的web mvc应用程序创建引擎,它处理我需要的所有内容,依赖注入,我只希望这个实例的一个实例存在。 – 2012-03-31 12:36:28

回答

2

基本上,你已经给你的singleton类和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>(); 

如果你想走得更远,你也可以指定由此提供程序创建的对象的生命周期,以便您可以使用单例,或让提供程序返回eac的新对象h呼叫等等。

您还应该看看依赖注入模式,它看起来接近您想要实现的目标,并且还查看已经执行此操作的现有DI框架(NInject,Nhibernate)等等。

2

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

internal static readonly T instance = new T(); 

您的代码假设要创建该类的实例,但不能实例化接口类型。

所以,如果你需要某种类型的充当singletone,你应该写:

Singleton<MyInterface>.Instance 

其中

public class MyInterface : IMyInterface { } 

然后,你不需要有任何“如果”在你的代码,因为Singleton负责将瞬间对象保持为一个实例。

与问题无关:目前Singletone's被许多开发者视为'代码嗅觉',所以一般来说你必须避免它们。尝试不考虑Singletone的应用程序。

+0

我有一个引擎,我想在整个mvc应用程序中使用。它处理像依赖注入,获取当前用户等东西。这就是为什么我想要一个引擎。 – 2012-03-31 13:51:44

+0

考虑到你的评论,我会说 - 你在MVC应用程序中做了一些真正错误的事情。如果你在应用程序中有IoC容器,他们中的很多人都有InSingletoneScope()策略来创建对象。你不需要创建你自己的。 – 2012-03-31 15:46:12