Other answers are great but I would also like to point out this Implementing Dependency Injection using Ninject article.
This is one of the best articles I ever read which explains Dependency Injection and Ninject with a very elegant example.
Here's the snippet from the article:
Below Interface will be implemented by our (SMSService) and (MockSMSService), basically the new Interface (ISMSService) will expose the same behaviors of both services as the code below:
public interface ISMSService
{
void SendSMS(string phoneNumber, string body);
}
(SMSService) implementation to implement the (ISMSService) interface:
public class SMSService : ISMSService
{
public void SendSMS(string mobileNumber, string body)
{
SendSMSUsingGateway(mobileNumber, body);
}
private void SendSMSUsingGateway(string mobileNumber, string body)
{
/*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
(MockSMSService) with totally different implementation using the same interface:
public class MockSMSService :ISMSService
{
public void SendSMS(string phoneNumber, string body)
{
SaveSMSToFile(phoneNumber,body);
}
private void SaveSMSToFile(string mobileNumber, string body)
{
/*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
we need to implement a change to our (UIHandler) class constructor to pass the dependency through it, by doing this, the code which uses the (UIHandler) can determine which concrete implementation of (ISMSService) to use:
public class UIHandler
{
private readonly ISMSService _SMSService;
public UIHandler(ISMSService SMSService)
{
_SMSService = SMSService;
}
public void SendConfirmationMsg(string mobileNumber) {
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
Now, we have to create a separate class (NinjectBindings) which inherits from (NinjectModule). This class will be responsible to resolve dependencies at run time, then we’ll override the load event which is used to configure the binding in it. The nice thing about Ninject is that we do not need to change our code in (ISMSService), (SMSService), and (MockSMSService).
public class NinjectBindings : Ninject.Modules.NinjectModule
{
public override void Load()
{
Bind<ISMSService>().To<MockSMSService>();
}
}
Now in UI form code, we’ll use the binding for Ninject which will determine which implementation to use:
class Program
{
static void Main(string[] args)
{
IKernel _Kernal = new StandardKernel();
_Kernal.Load(Assembly.GetExecutingAssembly());
ISMSService _SMSService = _Kernal.Get<ISMSService>();
UIHandler _UIHandler = new UIHandler(_SMSService);
_UIHandler.SendConfirmationMsg("96279544480");
Console.ReadLine();
}
}
Now the code is using the Ninject Kernal to resolve all chain of dependencies, if we want to use the real service (SMSService) in Release mode (on production environment) instead of the mock one, we need to change on the Ninject binding class (NinjectBindings) only to use the right implementation or by using the #if DEBUG directive as below:
public class NinjectBindings : Ninject.Modules.NinjectModule
{
public override void Load()
{
#if DEBUG
Bind<ISMSService>().To<MockSMSService>();
#else
Bind<ISMSService>().To<SMSService>();
#endif
}
}
Now our binding class (NinjectBindings) is living on the top of all our execution code and we can control the configuration easily in once place.
Also, see What is Inversion of Control? some very simple examples are mentioned to understand IoC.