接口和抽象类在多继承概念上的应用方式
- 接口的伪多继承特性:
- 在C#中,类不能直接继承多个类,但可以实现多个接口。接口只定义方法签名,不包含方法的实现(除了C# 8.0及以后可包含默认实现)。这使得一个类能从多个接口“继承”行为,模拟多继承效果。
- 例如:
public interface IPrintable
{
void Print();
}
public interface IStorable
{
void Store();
}
public class Document : IPrintable, IStorable
{
public void Print()
{
Console.WriteLine("Printing document...");
}
public void Store()
{
Console.WriteLine("Storing document...");
}
}
- 这里
Document
类实现了IPrintable
和IStorable
接口,从而具备了两种不同的行为,类似从多个“父类”继承功能。
- 抽象类在多继承概念中的作用:
- 抽象类可以包含抽象方法和具体方法。抽象类主要用于定义一些具有共性的行为和属性,子类继承抽象类并实现抽象方法。虽然不能像接口那样实现“多继承”行为,但在继承体系中起到了重要的抽象和规范作用。
- 例如:
public abstract class Shape
{
public abstract double Area();
public void PrintInfo()
{
Console.WriteLine($"This is a shape with area: {Area()}");
}
}
public class Circle : Shape
{
private double radius;
public Circle(double radius)
{
this.radius = radius;
}
public override double Area()
{
return Math.PI * radius * radius;
}
}
Shape
抽象类定义了Area
抽象方法和PrintInfo
具体方法,Circle
类继承Shape
并实现Area
方法,体现了抽象类在继承体系中的规范和复用作用。
在依赖注入框架中的角色和使用原理
- 接口在依赖注入中的角色和原理:
- 接口在依赖注入中起到了契约的作用。依赖注入框架通过接口来解析和注入具体的实现类。
- 例如,使用
Microsoft.Extensions.DependencyInjection
:
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[ConsoleLogger] {message}");
}
}
public class MyService
{
private readonly ILogger logger;
public MyService(ILogger logger)
{
this.logger = logger;
}
public void DoWork()
{
logger.Log("Doing some work...");
}
}
class Program
{
static void Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ILogger, ConsoleLogger>();
serviceCollection.AddTransient<MyService>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var myService = serviceProvider.GetService<MyService>();
myService.DoWork();
}
}
- 这里
MyService
依赖ILogger
接口,依赖注入框架根据配置将ConsoleLogger
注入到MyService
中。通过接口,MyService
不需要关心具体的日志实现类,实现了松耦合。
- 抽象类在依赖注入中的角色和原理:
- 抽象类在依赖注入中可用于定义一些通用的抽象行为,具体实现类继承抽象类。依赖注入框架同样可以根据抽象类类型来注入具体的子类实例。
- 例如:
public abstract class LoggerBase
{
public abstract void Log(string message);
}
public class FileLogger : LoggerBase
{
public override void Log(string message)
{
File.AppendAllText("log.txt", $"[FileLogger] {message}\n");
}
}
public class AnotherService
{
private readonly LoggerBase logger;
public AnotherService(LoggerBase logger)
{
this.logger = logger;
}
public void DoOtherWork()
{
logger.Log("Doing other work...");
}
}
class Program2
{
static void Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<LoggerBase, FileLogger>();
serviceCollection.AddTransient<AnotherService>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var anotherService = serviceProvider.GetService<AnotherService>();
anotherService.DoOtherWork();
}
}
AnotherService
依赖LoggerBase
抽象类,框架将FileLogger
注入。抽象类在这里提供了一种抽象层次,使得具体实现类能在依赖注入中以统一的抽象类型被处理。
在依赖注入场景下灵活运用接口和抽象类实现松耦合、可维护性高的软件架构
- 使用接口实现松耦合:
- 接口定义了清晰的契约,不同模块通过接口进行交互。例如在一个分层架构中,业务逻辑层的服务可以依赖数据访问层的接口。
public interface IUserRepository
{
User GetUserById(int id);
}
public class UserService
{
private readonly IUserRepository userRepository;
public UserService(IUserRepository userRepository)
{
this.userRepository = userRepository;
}
public User GetUser(int id)
{
return userRepository.GetUserById(id);
}
}
- 这样,业务逻辑层(
UserService
)与数据访问层的具体实现解耦,数据访问层可以有不同的实现类(如基于数据库、基于文件等),只需要实现IUserRepository
接口即可,方便替换和维护。
- 结合抽象类和接口提高可维护性:
- 可以定义抽象类来提供一些通用的功能或属性,再结合接口来定义行为契约。
public abstract class RepositoryBase<T>
{
// 一些通用的数据库连接等操作可以放在这里
protected string connectionString;
public RepositoryBase(string connectionString)
{
this.connectionString = connectionString;
}
}
public interface IUserRepository : RepositoryBase<User>
{
User GetUserById(int id);
}
public class SqlUserRepository : RepositoryBase<User>, IUserRepository
{
public SqlUserRepository(string connectionString) : base(connectionString)
{
}
public User GetUserById(int id)
{
// 使用 connectionString 进行数据库查询返回用户
// 实际实现省略
return null;
}
}
- 这里
RepositoryBase
抽象类提供了一些通用功能,IUserRepository
接口定义了具体行为,SqlUserRepository
继承抽象类并实现接口。这种方式既利用了抽象类的复用性,又通过接口实现了松耦合,提高了软件架构的可维护性。