C# 什么是Ninject?您何时使用它?

C# 什么是Ninject?您何时使用它?,c#,dependency-injection,ninject,C#,Dependency Injection,Ninject,我一直在帮助一些朋友做一个项目,有一个类使用Ninject。我对C#相当陌生,我不知道那门课在做什么,这就是为什么我需要理解Ninject。有人能解释什么是Ninject,什么时候使用它吗(如果可能,举个例子)?或者,如果你可以指向一些链接,那也会很好 我试着问这个问题:但对于像我这样的初学者来说,它并没有真正的帮助 Ninject是控制容器的反转 它有什么作用 假设您有一个汽车类,它依赖于驾驶员类 public class Car { public Car(IDriver driver

我一直在帮助一些朋友做一个项目,有一个类使用Ninject。我对C#相当陌生,我不知道那门课在做什么,这就是为什么我需要理解Ninject。有人能解释什么是Ninject,什么时候使用它吗(如果可能,举个例子)?或者,如果你可以指向一些链接,那也会很好


我试着问这个问题:但对于像我这样的初学者来说,它并没有真正的帮助

Ninject是控制容器的反转

它有什么作用

假设您有一个
汽车
类,它依赖于
驾驶员

public class Car 
{
   public Car(IDriver driver)
   {
      ///
   }
}
为了使用
Car
类,您可以这样构建它:

IDriver driver = new Driver();
var car = new Car(driver);
IoC容器集中了关于如何构建类的知识。它是一个知道一些事情的中央存储库。例如,它知道构建汽车所需的具体类是
驾驶员
,而不是任何其他
IDriver

例如,如果您正在开发MVC应用程序,您可以告诉Ninject如何构建控制器。您可以通过注册哪些具体类满足特定接口来实现。在运行时,Ninject将找出构建所需控制器所需的类,以及所有幕后操作

// Syntax for binding
Bind<IDriver>().To<Driver>();
有一些完整的框架负责为您自动创建测试类,它们被称为模拟框架

有关更多信息:


Ninject是.NET的依赖注入工具,是模式依赖注入(控制模式反转的形式)的实际实现

假设您有两个类
DbRepository
Controller

class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

class DbRepository {
   // ... some bussiness logic here ...
}
现在你有两个问题:

  • 必须初始化存储库才能使用它。您有几个选项可以执行此操作:

  • 手动,在构造函数中。但是,如果DbRepository的构造函数发生了变化怎么办?您需要重写
    控制器
    类,因为它所依赖的代码已更改。如果您只有一个
    控制器
    ,这并不难,但是如果您有几个类依赖于
    存储库
    ,那么您就有一个真正的问题
  • 您可以使用服务定位器或工厂。但是现在您依赖于服务定位器。您有一个全局服务定位器,所有代码都必须使用它。当您需要在代码的一部分中使用服务定位器进行激活逻辑,但在代码的另一部分中使用其他内容时,您将如何更改服务定位器的行为?只有一种方法——通过构造函数传递服务定位器。但是随着课程的增多,你需要通过考试的次数也越来越多。无论如何,这是个好主意,但从长远来看,这是个坏主意

    class Controller {
       private DbRepository _repository;
    
       public Controller() {
         _repository = GlobalServiceLocator.Get<DbRepository>()
       }
    
       // ... some methods that uses _repository
    }
    
    现在,当您需要控制器时,您可以编写:
    ninjectDevKernel.Get()
    ninjectTestKernel.Get()。您可以随心所欲地快速切换依赖项解析程序。看见很简单,你不需要写很多

  • 您不能为它创建单元测试。您的
    控制器
    依赖于
    DbRepository
    ,如果您想测试使用存储库的某些方法,您的代码将转到数据库并向其请求数据。太慢了,太慢了。如果
    DbRepository
    中的代码发生更改,则
    Controller
    上的单元测试将失败。在这种情况下,只有集成测试必须警告您“问题”。单元测试中需要的是隔离类,并在一个测试中只测试一个类(理想情况下,只测试一个方法)。如果您的
    DbRepository
    代码失败,您会认为
    Controller
    代码失败-这很糟糕(即使您对
    DbRepository
    Controller
    进行了测试-它们都会失败,您可以从错误的位置开始)。确定错误的真正位置需要很多时间。你需要知道A类是可以的,B类是失败的

  • 当您想在所有类中用其他内容替换
    DbRepository
    时,您需要做大量的工作

  • 您无法轻松控制
    DbRepository
    的生存期。此类的对象在初始化
    Controller
    时创建,并在删除
    Controller
    时删除。
    控制器
    类的不同实例之间没有共享,其他类之间也没有共享。使用Ninject,您可以简单地编写:

    kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
    
    kernel.Bind().To().InSingletonScope();
    
  • 依赖注入的一个特点是敏捷开发!您描述您的控制器使用具有接口
    IRepository
    的存储库。您不需要编写
    DbRepository
    ,只需创建一个
    MemoryRepository
    类并在另一个人开发
    DbRepository
    时开发
    Controller
    。当
    DbRepository
    的工作完成后,您只需在依赖项解析器中重新绑定默认的
    IRepository
    现在是
    DbRepository
    。有很多控制器吗?他们现在都将使用
    DbRepository
    。那很酷

    阅读更多:


  • 其他答案很好,但我也想指出这篇文章。
    这是我读过的最好的文章之一,它用一个非常优雅的例子解释了依赖注入和Ninject

    以下是文章的片段:

    下面的接口将由我们的(SMSService)和(MockSMSService)实现,基本上新接口(ISMService)将公开这两个服务的相同行为,如下代码所示:

    public interface ISMSService
     {
     void SendSMS(string phoneNumber, string body);
     }
    
    (SMSService)实现实现(ISMService)接口:

    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);
     }
     }
    
    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);
     }
     }
    
    (MockSMSService)使用同一接口实现完全不同:

    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);
     }
     }
    
    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);
     }
     }
    
    我们需要对(UIHandler)cla进行更改
    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!");
     }
     }
    
    public class NinjectBindings : Ninject.Modules.NinjectModule
     {
     public override void Load()
     {
     Bind<ISMSService>().To<MockSMSService>();
     }
     }
    
    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();
     }
     }
    
    public class NinjectBindings : Ninject.Modules.NinjectModule
     {
     public override void Load()
     {
    #if DEBUG
     Bind<ISMSService>().To<MockSMSService>();
    #else
     Bind<ISMSService>().To<SMSService>();
    #endif
    
    }
     }
    
    public interface IService
    {
        void Serve();
    }
    public class Service1 : IService
    {
        public void Serve() {
            Console.WriteLine("Service1 Called");
        }
    }
    public class Service2 : IService
    {
        public void Serve() {
            Console.WriteLine("Service2 Called");
        }
    }
    public class Service3 : IService
    {
        public void Serve() {
            Console.WriteLine("Service3 Called");
        }
    }
    public class Client
    {
        private IService service;
    
        public Client(IService _service)   //Constructor injection
        {
            service = _service;
        }
        public void ServeMethod() {
            service.Serve();  //Notice here, this Serve() method has no idea what to do.
        }                     // runtime will assign the object, that is Ninject
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            IService s1 = new Service1();  //N.B. Ninject assigns object with interface 
    
            Client c1 = new Client(s1);    
    
            c1.ServeMethod();         
    
            IService s2 = new Service2();  //N.B. Ninject assigns object with interface
    
            c1 = new Client(s2);
    
            c1.ServeMethod();
    
            IService s3 = new Service3(); //N.B. Ninject assigns object with interface
    
            c1 = new Client(s3);
    
            c1.ServeMethod();
    
    
            Console.ReadKey();
        }
    }       
      // Ninject creates object in runtime for interface in runtime in ASP.NET MVC project.