1.不使用框架如何手写依赖注入?
核心组件:
- 服务描述符(ServiceDescriptor);
- 服务集合(IServiceCollection);
- 服务提供者(IServiceProvider);
- 服务作用域(IServiceScope);
实现步骤:
a. 定义服务生命周期(Singleton, Scoped, Transient)
b. 实现服务描述符存储
c. 实现服务解析逻辑
d. 实现构造函数注入
e. 实现作用域管理
f. 添加循环依赖检测
g. 实现IDisposable释放资源
关键算法:
构造函数选择:选择参数最多的构造函数
依赖解析:递归解析所有依赖
生命周期管理:单例字典、作用域字典
循环依赖检测:使用HashSet记录解析路径
进阶功能:
属性注入;
延迟加载(Lazy);
工厂方法注册;
服务验证;
配置集成;
2.资源释放时必须要实现IDispoled里面的哪个方法?
"实现IDisposable接口时,必须实现Dispose()方法,因为这是接口的唯一方法签名。但通常我们采用标准Dispose模式:
1.必须实现Dispose()方法 - 这是接口的契约;
2.推荐实现Dispose(bool disposing) - 用于区分托管和非托管资源;
3.可选实现终结器 - 只有非托管资源时需要;
csharp
public void Dispose() // 必须实现
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) // 推荐
{
if (!disposed)
{
if (disposing)
{
// 释放托管资源
}
// 释放非托管资源
}
}
~MyClass() // 可选
{
Dispose(false);
}
3.EFCore和ADO.NET的安全性?
EF Core 和 ADO.NET 的安全性都基于参数化查询防止SQL注入。EF Core 自动参数化LINQ查询,但原生SQL需要谨慎。ADO.NET 需要手动参数化。两者都需要安全存储连接字符串、最小权限原则、输入验证和数据加密。
- SQL注入防护:
EF Core:LINQ自动参数化,FromSqlRaw需要手动参数化;
ADO.NET:必须手动使用Parameters; - 连接安全:
使用集成认证而非明文密码;
从安全存储获取连接字符串;
启用连接加密; - 数据保护:
加密敏感字段;
审计追踪;
输入验证和清理; - 权限控制:
数据库最小权限用户;
行级/列级安全;
应用层授权; - 监控审计:
记录慢查询;
检测攻击模式;
安全日志;
4.扩展方法的实现?
扩展方法是C# 3.0引入的特性,允许在不修改源代码的情况下,为现有类型添加新方法。它们通过静态类和静态方法实现,第一个参数使用this关键字指定要扩展的类型。
csharp
// 1. 创建静态类
public static class StringExtensions
{
// 2. 创建静态方法
// 3. 第一个参数用this关键字
public static bool IsNullOrEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
}
扩展方法的限制:
只能扩展方法,不能扩展属性、事件、运算符等;
不能访问类型的私有成员;
如果与实例方法同名,实例方法优先;
只能定义在静态类中;
5.foreach里面能不能修改数据?根本原因是什么?运行时报错还是编译报错?
foreach循环内部使用迭代器模式,编译器会将其转换为GetEnumerator()和MoveNext()的调用。集合内部维护一个版本号,每次修改集合结构时版本号会增加。迭代器在创建时记录当前版本,每次迭代时检查版本是否一致,如果不一致就抛出InvalidOperationException。
编译时错误:尝试给值类型的迭代变量赋值;
运行时错误:添加/删除元素;
允许的操作:修改引用类型元素的属性;
安全修改集合的方法:
使用for循环倒序遍历;
使用ToList()创建副本;
使用LINQ的RemoveAll;
使用临时列表记录要修改的元素;
csharp
var enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
// 循环体
}
集合的迭代器在MoveNext()中会检查集合是否被修改。以List为例,它内部有一个version字段,每次修改结构时version++。迭代器创建时记录当前_version,每次MoveNext()时检查版本是否变化。
这种设计保证了在迭代过程中集合结构的一致性,防止出现不可预测的行为。
常见误区:
foreach中使用continue或break是安全的;
修改数组元素属性是安全的;
可以安全地修改嵌套集合中的元素;
线程安全集合也不能在foreach中修改;
6.Predicate委托是什么?
Predicate 是一个泛型委托,它表示一个接收T类型参数并返回bool值的方法。定义是:public delegate bool Predicate(T obj)。
主要作用:
- 在集合操作中作为条件判断(如List的Find、FindAll、RemoveAll等方法);
2.实现策略模式,动态改变算法行为;
3.作为过滤条件,筛选数据;
Predicate委托详解
Predicate是C#中一个非常重要的泛型委托,用于表示一个返回布尔值的方法,通常用于定义一组条件。
"Predicate 是一个泛型委托,它表示一个接收T类型参数并返回bool值的方法。定义是:public delegate bool Predicate(T obj);"
主要用途:
"1. 在集合操作中作为条件判断(如List的Find、FindAll、RemoveAll等方法);
实现策略模式,动态改变算法行为;
作为过滤条件,筛选数据;
Predicate 和 Func<T, bool> 的区别:
它们在功能上是等价的,都表示接收T参数返回bool的方法。主要区别在于:
历史原因:Predicate是.NET 2.0引入的,Func<T, bool>是.NET 3.5引入的
使用场景:集合类通常使用Predicate,LINQ通常使用Func<T, bool>
语义差异:Predicate强调'谓词'、'条件判断'的含义"。
csharp
// 定义Predicate
Predicate<int> isEven = x => x % 2 == 0;
// 使用Predicate
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
List<int> evens = numbers.FindAll(isEven);
// 组合Predicate
Predicate<int> isPositive = x => x > 0;
Predicate<int> isEvenAndPositive = isEven.And(isPositive);