简体   繁体   中英

The Dependency Inversion Principle with .NET Framework classes

I'm trying to understand SOLID principles, in particular The Dependency Inversion Principle .

In this is SO answer it is explained very well.

I think I have understood that I can't create any instance of a class inside my class. Is it right?

But if I have to save to disk some content, can I create an instance of System.IO.File or do I have to inject it?

I don't understand where is the limit, if I can't instance my own classes or if I can't either instance .NET Framework classes (or whatever other framework).

UPDATE :
I think File is a bad example because is declared as static.

By the way, does this principle apply to static classes ?

The S of SOLID stands for SRP (Single Responsibility Principle). You won't violate it by using System.IO.File inside a class directly, once you keep that class with one single responsibility.

It's a good idea trying to abstract the purpose behind using System.IO.File . Let's suppose you need it to generate a log. Then you would probably do something like:

public interface IMyLogger
{
    void GenerateLog(IEnumerable<string> content);
}

public class FileLogger: IMyLogger
{
    public void GenerateLog(IEnumerable<string> content)
    {
        System.IO.File.WriteAllLines("C:/Log", content);
    }
}

Maybe it's not just a log, it's something more important, like generating a file so other system/app (even external) read it and do some job.

If you are trying to use a DDD approach, the interface could belong to your domain, and the implementation could belong in the application. Then you register your interface as a service and inject it.

The class which needs an IMyLogger actually doesn't need to know how is the log being generated, it just needs the job to be done.

You can apply the same idea when you need to send an email inside some business logic in your domain. Instead of making a connection to an Exchange inside your domain directly, create an interface INotifier and a MailNotifier implementing it to be injected.

Somewhere down the chain of dependencies you will need to use the concrete class directly. Even if you use a DI framework like Ninject, the framework itself will create an instance of the concrete type, so it will not be injected into the framework (which wouldn't make sense, of course).

You can only abstract something away to a certain level. It will vary from project to project - you have to ask yourself if you need another level of abstraction (be it for modularity, unit testing etc.). I think this is very important - you want to be pragmatic, not create layers upon layers of abstractions just for the sake of it.

By the way, does this principle apply to static classes?

Yes, it does. But with static classes you have to introduce a wrapper, which will delegate calls to the static class, because a static class cannot implement interfaces.

There is no point in applying a principle just for the sake of it. Think in a pragmatic way.

If you want to unit-test a method that uses hard coded file accesses, your unit tests will access these files. This is usually not desired as you must set up or clean up these files. To avoid this, you would inject a service which wraps these file accesses. This allows you to replace the real service with a fake one during the unit tests. This fake service can provide hard coded test data for read accesses and dump written data to memory for later analysis or simply do nothing. Btw.: NSubstitute can create fake services at runtime easily.

The injection of services allows you to achieve Unit Test Isolation. Eg you can test some logic without depending on correct file handling or database accesses or the correct functioning of other services. Injecting a service is just one way to do it. You could also just specify a method parameter as IEnumerable<string> with the content of the file instead. Events can also be used for decoupling. Instead of writing to a log, you could raise a log event.


Most DI frameworks allow you to specify the lifetime of objects. One of these options is Singleton , which means that the DI container will always return the same instance of a requested service. This allows you to wrap static classes in a service that behaves statically.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM