views:

286

answers:

2

I'm using Ninject 2.0 for the .Net 3.5 framework. I'm having difficulty with singleton binding.

I have a class UserInputReader which implements IInputReader. I only want one instance of this class to ever be created.

 public class MasterEngineModule : NinjectModule
    {
        public override void Load()
        {
            // using this line and not the other two makes it work
            //Bind<IInputReader>().ToMethod(context => new UserInputReader(Constants.DEFAULT_KEY_MAPPING));

            Bind<IInputReader>().To<UserInputReader>();
            Bind<UserInputReader>().ToSelf().InSingletonScope();
        }
    }

        static void Main(string[] args) 
        {
            IKernel ninject = new StandardKernel(new MasterEngineModule());
            MasterEngine game = ninject.Get<MasterEngine>();
            game.Run();
        }

 public sealed class UserInputReader : IInputReader
    {
        public static readonly IInputReader Instance = new UserInputReader(Constants.DEFAULT_KEY_MAPPING);

        // ...

        public UserInputReader(IDictionary<ActionInputType, Keys> keyMapping)
        {
            this.keyMapping = keyMapping;
        }
}

If I make that constructor private, it breaks. What am I doing wrong here?

A: 

IInputReader does not declare the Instance field nor can it, since interfaces can not have field or static field or even static properties (or static methods).

The Bind class can not know that it is to find the Instance field (unless it uses reflection).

Danny Varod
+1  A: 

Of course it breaks if you make the constructor private. You can't call private constructors from outside your class!

What you're doing is exactly what you're supposed to do. Test it:

var reader1 = ninject.Get<IInputReader>();
var reader2 = ninject.Get<IInputReader>();
Assert.AreSame(reader1, reader2);

You don't need the static field to get the instance singleton. If you're using an IoC container, you should get all your instances through the container.

If you do want the public static field (not sure if there is a good reason for that), you can bind the type to its value, like this:

Bind<UserInputReader>().ToConstant(UserInputReader.Instance);

EDIT: To specify the IDictionary<ActionInputType, Keys> to use in the constructor of UserInputReader, you can use the WithConstructorArgument method:

Bind<UserInputReader>().ToSelf()
     .WithConstructorArgument("keyMapping", Constants.DEFAULT_KEY_MAPPING)
     .InSingletonScope();
Martinho Fernandes
Ok, cool. But UserInputReader's constructor requires a `IDictionary<enum, enum>`. How can I specify this in Ninject?
Rosarch
@Rosarch: either you create a marker wrapper around the dictionary, call it `IActionKeyMap` or something, and use that, or you use the method approach in your comment, or you use the facilities to specify ctor parameters (can't remember exactly how :(.
Martinho Fernandes
Got it: `.WithConstructorArgument("keyMapping", /* whatever */);`
Martinho Fernandes