Dependency Injection是否仅适用于接口?抽象类场景算依赖注入吗?
Great question! This is a super common misconception about dependency injection (DI), so let's break it down clearly:
The Core of Dependency Injection
First, let's get back to what DI actually is. At its heart, DI is a pattern that implements Inversion of Control (IoC)—meaning a class doesn't create its own dependencies; instead, those dependencies are passed (injected) from outside the class. The key principle here is depending on abstractions rather than concrete implementations—but that abstraction doesn't have to be an interface.
Abstract Classes Work Just as Well for DI
Your second scenario (using an abstract class and its subclasses) absolutely counts as dependency injection. Let's use code examples to illustrate:
Interface-Based DI (the common case)
// Interface as the abstraction public interface IMessageSender { void Send(string message); } public class EmailSender : IMessageSender { public void Send(string message) { // Logic to send email } } // Class receiving the injected dependency public class NotificationService { private readonly IMessageSender _sender; // Constructor injection public NotificationService(IMessageSender sender) { _sender = sender; } }
Abstract Class-Based DI (equally valid)
// Abstract class as the abstraction public abstract class BaseMessageSender { // Abstract method requiring implementation public abstract void Send(string message); // Optional: Default implementation for shared logic protected void ValidateMessage(string message) { if (string.IsNullOrEmpty(message)) throw new ArgumentException("Message cannot be empty"); } } public class SmsSender : BaseMessageSender { public override void Send(string message) { ValidateMessage(message); // Logic to send SMS } } // Class receiving the injected abstract class dependency public class AlertService { private readonly BaseMessageSender _sender; // Constructor injection (same pattern as interface) public AlertService(BaseMessageSender sender) { _sender = sender; } }
In the abstract class example, AlertService depends on the BaseMessageSender abstraction, not the concrete SmsSender. We can swap in any subclass of BaseMessageSender (like a PushNotificationSender) without modifying AlertService—this is exactly the loose coupling that DI is designed to enable.
Even Concrete Classes Can Be Injected (Though Not Recommended)
To go a step further: technically, you can inject a concrete class and still be using DI. For example:
public class ConcreteLogger { public void Log(string message) => Console.WriteLine(message); } public class ReportService { private readonly ConcreteLogger _logger; public ReportService(ConcreteLogger logger) { _logger = logger; } }
This is still DI because the ConcreteLogger is provided externally, not created inside ReportService. However, this tightens coupling (you can't swap the logger easily), so it's not a best practice. But it's still valid DI.
Final Verdict
Dependency Injection is not limited to interfaces. Any scenario where a class receives its dependencies from an external source—whether those dependencies are interfaces, abstract classes, or even concrete classes—qualifies as DI. The best practice is to depend on abstractions (interfaces or abstract classes) for flexibility, but the pattern itself doesn't restrict you to interfaces.
内容的提问来源于stack exchange,提问作者padrian92

