Skip to content

C#开发工程师(面试260308)

1. 什么是 CTSCLSCLR

解答

CTS(通用类型系统)

  • 定义 .NET 全部数据类型与行为规范
  • 统一多语言类型,保障跨语言兼容
  • 核心分类:值类型引用类型

CLS(通用语言规范)

  • CTS 的子集,跨语言开发最小约束
  • 屏蔽语言差异(如大小写、关键字)
  • 标记[CLSCompliant]即可合规开发

CLR(公共语言运行时)

  • .NET 核心执行引擎
  • 负责IL解析、JIT编译、GC回收、线程管理、安全校验
  • 主流版本:CoreCLRDesktop CLR
csharp
// CLS 合规代码演示
using System;
[assembly: CLSCompliant(true)]
public class TypeDemo
{
    // CTS 标准类型
    public System.Int32 Num { get; set; }
    public System.String Text { get; set; }
}

2. CLR 技术和 COM 技术的比较

解答
特性CLRCOM
内存管理GC自动回收手动引用计数
跨平台全平台跨端仅限Windows
版本控制并行部署无DLL冲突经典DLL Hell
类型系统统一CTS类型独立类型库
注册机制无需注册表强制系统注册表注册
csharp
// CLR 轻量化开发示例
public class ClrDemo
{
    public int Calc(int a,int b)=>a+b;
}
// 无需手动释放、注册,GC自动管理内存

3. JIT 是如何工作的

解答

JIT(即时编译)CLR 核心编译机制,核心流程:

  1. 源码编译:C# 代码编译为IL中间语言,存入程序集
  2. 运行加载:程序启动加载IL代码,不提前编译
  3. 即时编译:方法首次调用时,实时编译为当前CPU机器码
  4. 缓存复用:编译后代码缓存,后续直接执行,提升性能
  5. 分层优化:.NET 新增分层编译,平衡启动速度与运行效率
csharp
public class JitTest
{
    // 首次调用触发JIT编译,二次调用走缓存
    public static int Run(int x)=>x*x+1;
}

4. 怎么把程序放入 GAC

解答

GAC(全局程序集缓存):Windows 全局共享程序集目录

前置条件

程序集必须强名称签名

操作步骤

  1. 生成强名称密钥
powershell
sn -k Key.snk
  1. 项目配置签名
xml
<PropertyGroup>
  <SignAssembly>true</SignAssembly>
  <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
  1. 命令行安装GAC
powershell
gacutil /i Demo.dll
csharp
// 代码方式安装GAC
using System.EnterpriseServices.Internal;
public static void InstallGac(string path)
{
    new Publish().GacInstall(path);
}

备注:.NET Core/.NET5+ 不再支持GAC,改用NuGet共享

5. 值类型引用类型的区别

解答
维度值类型引用类型
存储位置StackHeap
赋值逻辑拷贝完整值拷贝内存地址
回收方式栈自动释放GC垃圾回收
默认值零值(0/false)null
基类ValueTypeobject
csharp
// 值类型示例
int a = 10;
int b = a; b=20; // a不变

// 引用类型示例
int[] arr1 = new int[]{1};
int[] arr2 = arr1; arr2[0]=99; // arr1同步修改

6. C#中stringString 有什么区别

解答
  1. 本质完全一致:编译后均为System.String
  2. string:C# 内置关键字别名,日常开发推荐
  3. String:.NET 框架完整类名,适合调用静态方法
  4. IL代码完全一致,无性能差异
csharp
string str1 = "test";
String str2 = "demo";
// 二者类型完全等同
Console.WriteLine(str1.GetType() == typeof(String));

7. .NET中堆栈和堆的特点和差异

解答

栈 Stack

  • 线程私有,内存连续,分配速度快
  • 存储:值类型、对象引用地址
  • 方法执行结束自动释放,无GC

堆 Heap

  • 进程共享,内存碎片化,分配较慢
  • 存储:引用类型实例、数组
  • 依赖GC标记-清除-压缩机制回收
csharp
public void MemTest()
{
    int num = 10; // 栈内存
    string s = new string("123"); // 引用在栈,实例在堆
}

8. .NET中 GC 的运行机制

解答

GC(垃圾回收器)CLR 自动内存管理核心

  1. 三大阶段
    • 标记:遍历根对象,标记存活对象
    • 清除:回收未标记垃圾内存
    • 压缩:整理内存,减少碎片
  2. 分代回收
    • 0代:新建短期对象,回收频率最高
    • 1代:缓冲过渡区域
    • 2代:长期常驻对象,回收频率极低
  3. 触发时机:内存不足、手动调用、程序退出
csharp
// 手动触发GC(不推荐生产使用)
GC.Collect();
GC.WaitForPendingFinalizers();

9. 简述 C#中重写、重载和隐藏的概念

解答
  1. 重载 Overload 同类别名方法,参数个数/类型不同,编译时多态
  2. 重写 Override 子类重写virtual虚方法,运行时多态,完全覆盖逻辑
  3. 隐藏 New 子类隐藏父类同名方法,无多态,按引用类型调用
csharp
public class Base
{
    public virtual void Show(){}
    public void Print(){}
}
public class Son : Base
{
    public override void Show(){} // 重写
    public new void Print(){} // 隐藏
    public void Add(int a){}
    public void Add(int a,int b){} // 重载
}

10. C#如何声明类不能被继承

解答

使用 sealed 密封关键字

  1. 密封类:禁止被继承
  2. 密封方法:禁止子类重写
csharp
// 密封类,无法继承
public sealed class FinalClass{}

// 密封重写方法
public class A
{
    public virtual void Test(){}
}
public class B:A
{
    public sealed override void Test(){}
}

11. Int[]是引用类型还是值类型

解答

int[] 属于引用类型

  1. 所有数组都继承自System.Array,属于引用类型
  2. 数组引用存栈,数组实例数据存堆
  3. 赋值时仅拷贝引用地址,共用同一组数据
csharp
// 验证数组类型
Console.WriteLine(typeof(int[]).IsClass); // True

12. 解释泛型的基本原理

解答

泛型 Generics:通过类型参数实现代码复用 核心优势:类型安全、避免装箱拆箱、高性能 核心原理:编译时保留类型约束,运行时针对值类型/引用类型差异化实例化

csharp
// 泛型类
public class DataList<T>
{
    public T Data { get; set; }
}
// 泛型约束
public static T Max<T>(T a,T b) where T:IComparable<T>

13. Serializable 特性有何作用

解答

[Serializable]:标记类支持二进制序列化

  1. 序列化:对象转字节流,用于存储/网络传输
  2. 反序列化:字节流还原对象
  3. 搭配[NonSerialized]可忽略不需要序列化的字段
csharp
[Serializable]
public class User
{
    public string Name { get; set; }
    [NonSerialized]
    public string TempData; // 不参与序列化
}

14. 如何自定义序列化和反序列化

解答

两种主流实现方式:

  1. 实现 ISerializable 接口,完全控制序列化逻辑
  2. 使用序列化回调特性:[OnSerializing]OnDeserialized
csharp
[Serializable]
public class CustomData : ISerializable
{
    public string Content { get; set; }
    // 自定义序列化
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Data",Content);
    }
    // 自定义反序列化
    protected CustomData(SerializationInfo info, StreamingContext context)
    {
        Content = info.GetString("Data");
    }
}

15. 如何使用 IFormattable 接口实现格式化输出

解答

实现 IFormattable 接口,重写ToString(string, IFormatProvider) 自定义格式符,实现多格式文本输出

csharp
public class Price : IFormattable
{
    public decimal Value { get; set; }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        return format switch
        {
            "C"=>Value.ToString("C"),
            "N"=>Value.ToString("N2"),
            _=>Value.ToString()
        };
    }
}

16. .NET提供了哪几个定时器类型

解答
定时器适用场景线程
Forms.TimerWinForm UIUI线程
DispatcherTimerWPFUI线程
Timers.Timer服务端、后台任务工作线程
Threading.Timer轻量底层定时线程池
csharp
// 常用后台定时器
var timer = new System.Timers.Timer(1000);
timer.Elapsed+=(s,e)=>Console.WriteLine("定时执行");
timer.Start();

17. System.Object 三个比较方法异同

解答
  1. ReferenceEquals:强制比较内存引用,不可重写
  2. Equals(object):默认引用比较,可重写为值比较
  3. 静态Equals:空值安全处理,内部调用实例Equals
csharp
string a = "123";
string b = "123";
Console.WriteLine(ReferenceEquals(a,b));// 驻留池为True
Console.WriteLine(a.Equals(b));// 值相等True

18. 请解释委托的基本原理

解答

Delegate委托:类型安全的方法指针

  1. 本质:继承MulticastDelegate的特殊类
  2. 核心要素:方法地址、目标实例
  3. 作用:回调方法、解耦代码、实现事件、链式调用
csharp
// 委托声明与使用
public delegate void MsgDelegate(string msg);
MsgDelegate del = Console.WriteLine;
del("委托调用");

19. 委托回调静态方法和实例方法有何区别

解答
  1. 静态委托:Target=null,仅绑定方法,无对象依赖
  2. 实例委托:绑定具体对象,Target指向实例,可访问成员
  3. 生命周期:实例委托随对象回收,静态委托常驻内存
csharp
public class Demo
{
    public static void StaticFun(){}
    public void InstanceFun(){}
}
// 静态委托
Action d1 = Demo.StaticFun;
// 实例委托
Action d2 = new Demo().InstanceFun;

20. 什么是链式委托

解答

链式委托(多播委托):一个委托绑定多个方法

  1. 通过 += 追加方法,-= 移除方法
  2. 调用时按添加顺序依次执行
  3. 返回值仅保留最后一个方法结果
  4. 单个方法异常会中断整个委托链
csharp
Action chain = Test1;
chain += Test2; // 链式绑定
chain(); // 依次执行两个方法

21. 请解释事件的基本使用方法

解答

Event事件:委托的封装版本,发布订阅模式

  1. 限制外部仅能+=/-=,防止委托覆盖
  2. 仅类内部可主动触发,安全性更高
  3. 标准结构:事件声明、触发方法、订阅绑定
csharp
public class EventDemo
{
    // 声明事件
    public event Action OnRun;
    // 触发事件
    public void Trigger()=>OnRun?.Invoke();
}
// 订阅
var e = new EventDemo();
e.OnRun+=()=>Console.WriteLine("事件触发");

22. 反射的基本原理和其实现的基石

解答

反射 Reflection:运行时动态获取程序集、类、方法、属性信息

  1. 实现基石:元数据(Metadata)
  2. 核心命名空间:System.Reflection
  3. 核心能力:动态创建对象、调用方法、修改属性、读取特性
csharp
// 反射基础示例
Type type = typeof(User);
var obj = Activator.CreateInstance(type);
var method = type.GetMethod("Show");
method.Invoke(obj,null);
最近更新