某公司C#-WPF面试题-来自nowcoder(含答案和解析)--2

1.不使用框架如何手写依赖注入?
核心组件:

  1. 服务描述符(ServiceDescriptor);
  2. 服务集合(IServiceCollection);
  3. 服务提供者(IServiceProvider);
  4. 服务作用域(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 需要手动参数化。两者都需要安全存储连接字符串、最小权限原则、输入验证和数据加密。

  1. SQL注入防护:
    EF Core:LINQ自动参数化,FromSqlRaw需要手动参数化;
    ADO.NET:必须手动使用Parameters;
  2. 连接安全:
    使用集成认证而非明文密码;
    从安全存储获取连接字符串;
    启用连接加密;
  3. 数据保护:
    加密敏感字段;
    审计追踪;
    输入验证和清理;
  4. 权限控制:
    数据库最小权限用户;
    行级/列级安全;
    应用层授权;
  5. 监控审计:
    记录慢查询;
    检测攻击模式;
    安全日志;
    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)。
主要作用:

  1. 在集合操作中作为条件判断(如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);
相关推荐
Joker 0073 小时前
Linux nohup命令实战指南
linux·运维·wpf
爱敲点代码的小哥5 小时前
类型转换 递归算法 编译错误 装箱和拆箱 知识点
开发语言·c#
时光追逐者6 小时前
一个 WPF 开源、免费的 SVG 图像查看控件
开源·c#·.net·wpf
江沉晚呤时7 小时前
构建智能代理的利器:深入解析 Microsoft Agent Framework
开发语言·c#
武藤一雄7 小时前
C# 中线程安全都有哪些
后端·安全·微软·c#·.net·.netcore·线程
wuguan_8 小时前
C#递推算法
算法·c#·递推算法
de之梦-御风8 小时前
【WebAPI 模拟器】.NET 8/9 + Minimal API + Swagger + DI + WPF Host
.net·wpf·web
nnsix9 小时前
【C#】HttpPost请求 - Query参数 - URL编码方法
java·javascript·c#
无风听海9 小时前
TaskFactory
服务器·开发语言·c#