当前位置: 首页 > news >正文

C#面试题及详细答案120道(01-10)-- 基础语法与数据类型

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

文章目录

  • 一、本文面试题目录
      • 1. 简述C#与.NET的关系
      • 2. C#的值类型和引用类型有什么区别?各包含哪些常见类型?
      • 3. 什么是装箱和拆箱?会带来什么性能影响?
      • 4. string和String、int和Int32有什么区别?
      • 5. 简述C#中的常量(const)和只读(readonly)的区别
      • 6. 什么是 nullable 类型?如何使用?
      • 7. ref和out关键字的作用及区别
      • 8. params关键字的用法
      • 9. C#中的访问修饰符有哪些?各自的作用范围是什么?
      • 10. 简述static关键字的用法(静态类、静态方法、静态变量)
  • 二、120道C#面试题目录列表

一、本文面试题目录

1. 简述C#与.NET的关系

C#是一种编程语言,而.NET是一个跨平台的开发框架,两者紧密关联但本质不同:

  • C#:由微软开发的面向对象编程语言,语法简洁、类型安全,支持多种编程范式(面向对象、泛型、函数式等)。
  • .NET:提供了运行时环境(CLR)、类库(FCL)和开发工具的框架,支持多种编程语言(C#、VB.NET、F#等)。

关系说明

  • C#是.NET生态中最主流的编程语言
  • C#代码需编译为中间语言(IL),再由.NET的CLR(公共语言运行时)执行
  • .NET为C#提供基础类库、内存管理、异常处理等核心功能

简单来说:C#是"工具",.NET是"工作台",C#依托.NET框架实现各种功能。

2. C#的值类型和引用类型有什么区别?各包含哪些常见类型?

核心区别:存储位置和内存管理方式不同

特性值类型引用类型
存储位置栈(Stack)堆(Heap)
内存分配编译时确定运行时动态分配
赋值方式复制值本身复制引用地址
内存释放超出作用域自动释放由GC(垃圾回收)管理

常见类型

  • 值类型

    • 基本数据类型:int、float、double、bool、char
    • 结构体:struct、enum
    • 其他:decimal、DateTime
  • 引用类型

    • 类:class
    • 接口:interface
    • 数组:Array
    • 字符串:string(特殊引用类型,具有不可变性)
    • 委托:delegate

示例代码

// 值类型示例
int a = 10;
int b = a;  // 复制值
b = 20;
Console.WriteLine(a);  // 输出 10(a不受b影响)// 引用类型示例
class MyClass { public int Value; }
MyClass x = new MyClass { Value = 10 };
MyClass y = x;  // 复制引用
y.Value = 20;
Console.WriteLine(x.Value);  // 输出 20(x和y指向同一对象)

3. 什么是装箱和拆箱?会带来什么性能影响?

装箱(Boxing):将值类型转换为引用类型的过程

  • 原理:在堆上分配内存,复制值类型的值到新分配的内存,返回引用

拆箱(Unboxing):将引用类型(已装箱的值类型)转换回值类型的过程

  • 原理:检查对象是否为指定值类型的装箱形式,将堆中的值复制到栈上

示例代码

int i = 10;          // 值类型(栈上)
object obj = i;      // 装箱:int -> object(堆上分配内存)
int j = (int)obj;    // 拆箱:object -> int(验证类型并复制值)

性能影响

  1. 装箱会导致堆内存分配和复制操作,比普通赋值慢约10-20倍
  2. 拆箱需要类型检查和值复制,也会消耗额外性能
  3. 频繁的装箱拆箱会增加GC压力,导致性能下降

优化建议

  • 使用泛型(如List)避免装箱
  • 尽量在编译时确定类型,减少运行时类型转换
  • 避免在循环等高频操作中进行装箱拆箱

4. string和String、int和Int32有什么区别?

本质区别:别名与实际类型的关系

  • int vs Int32

    • int是System.Int32的别名(C#关键字)
    • 两者完全等价,编译后生成的IL代码相同
    • 其他类似:long对应Int64,short对应Int16等
  • string vs String

    • string是System.String的别名(C#关键字)
    • 功能完全一致,但使用场景略有不同:
      • 声明字符串变量时推荐用string(语言关键字)
      • 调用静态方法时推荐用String(类名)

示例代码

// int与Int32等价
int a = 10;
Int32 b = 20;
int c = (Int32)30;  // 类型转换完全兼容// string与String等价
string s1 = "hello";
String s2 = "world";
bool isEqual = string.Equals(s1, s2);  // 推荐用string调用实例方法
bool isNullOrEmpty = String.IsNullOrEmpty(s1);  // 推荐用String调用静态方法

最佳实践

  • 保持代码风格一致,避免混合使用
  • 对于值类型,优先使用关键字形式(int、bool等)
  • 对于字符串操作,实例方法用string,静态方法用String

5. 简述C#中的常量(const)和只读(readonly)的区别

const(常量):编译时确定值的常量,声明后不可更改

  • 必须在声明时初始化
  • 只能用于值类型和string
  • 属于编译时常量,编译器会直接替换其值

readonly(只读):运行时确定值的常量,只能在构造函数中修改

  • 可以在声明时或构造函数中初始化
  • 可用于任何类型
  • 属于运行时常量,保留变量特性

示例代码

public class MyClass
{// 常量:编译时确定值public const int ConstValue = 100;// 只读字段:可在声明时初始化public readonly int ReadOnlyValue1 = 200;// 只读字段:可在构造函数中初始化public readonly int ReadOnlyValue2;public MyClass(){ReadOnlyValue2 = 300;  // 合法// ConstValue = 400;   // 错误:const不能在构造函数中赋值}public void MyMethod(){// ReadOnlyValue1 = 500;  // 错误:只读字段不能在方法中修改}
}

主要区别表格

特性constreadonly
初始化时机声明时必须初始化声明时或构造函数中
可修改性完全不可修改仅能在构造函数中修改
适用类型仅值类型和string所有类型
编译处理编译器直接替换值保留变量引用
静态特性隐式静态可静态可实例

6. 什么是 nullable 类型?如何使用?

nullable类型:允许值类型(如int、bool等)接受null值的特殊类型

  • 解决了值类型默认不能为null的限制
  • 语法:在值类型后加?,如int?等价于Nullable<int>

使用场景

  • 数据库交互(表示可空字段)
  • 需区分"未赋值"和"默认值"的场景
  • 可选参数或可选值

示例代码

// 声明可空类型
int? nullableInt = null;
bool? nullableBool = true;
DateTime? nullableDate = new DateTime(2023, 1, 1);// 赋值操作
nullableInt = 100;  // 可以赋值为正常值
nullableInt = null; // 也可以赋值为null// 检查是否有值
if (nullableInt.HasValue)
{int value = nullableInt.Value;  // 获取值Console.WriteLine($"值为: {value}");
}
else
{Console.WriteLine("值为null");
}// 空合并运算符(??)
int result = nullableInt ?? -1;  // 如果为null则使用-1
Console.WriteLine(result);  // 输出 -1(因为nullableInt当前为null)// 条件运算符(?.)
int? length = nullableDate?.Day;  // 安全访问,避免NullReferenceException

注意事项

  • 引用类型本身可null,无需使用nullable类型
  • 可空类型之间可以相互转换
  • 使用GetValueOrDefault()可获取值或默认值(不抛异常)

7. ref和out关键字的作用及区别

ref关键字:用于传递变量的引用,允许方法修改调用者的变量值

  • 要求变量在传递前必须初始化
  • 用于"传递并可能修改"已有变量

out关键字:用于传递变量的引用,强调方法必须为变量赋值

  • 变量在传递前可以不初始化
  • 用于"输出"多个返回值的场景

示例代码

// ref关键字示例
int x = 10;
ModifyWithRef(ref x);
Console.WriteLine(x);  // 输出 20void ModifyWithRef(ref int value)
{value *= 2;  // 修改原始变量的值
}// out关键字示例
int y;  // 可以不初始化
bool success = TryParseNumber("123", out y);
if (success)
{Console.WriteLine(y);  // 输出 123
}bool TryParseNumber(string input, out int result)
{// 方法必须为out参数赋值if (int.TryParse(input, out result)){return true;}result = 0;  // 即使失败也必须赋值return false;
}

主要区别

特性refout
初始化要求传递前必须初始化传递前可未初始化
赋值要求方法内可赋可不赋方法内必须赋值
主要用途传递并修改已有值返回多个结果值
数据流向双向(进和出)单向(仅出)

使用建议

  • 需要修改已有变量时用ref
  • 需要返回多个结果时用out(如TryXXX模式)
  • C# 7.0+支持out变量声明简化:TryParseNumber("123", out int y)

8. params关键字的用法

params关键字:允许方法接受数量可变的参数,这些参数被视为数组处理

  • 简化了调用方传递多个参数的语法
  • 每个方法只能有一个params参数,且必须是最后一个参数

使用场景

  • 不确定参数数量的方法(如字符串格式化、数学计算)
  • 希望提供更简洁调用方式的API

示例代码

// 定义带params参数的方法
public static int Sum(params int[] numbers)
{int total = 0;foreach (int num in numbers){total += num;}return total;
}// 调用方式
public static void Main()
{// 1. 传递多个参数int sum1 = Sum(1, 2, 3, 4);Console.WriteLine(sum1);  // 输出 10// 2. 传递数组int[] values = { 5, 6, 7 };int sum2 = Sum(values);Console.WriteLine(sum2);  // 输出 18// 3. 传递零个参数int sum3 = Sum();Console.WriteLine(sum3);  // 输出 0
}// 结合其他参数使用(params必须是最后一个)
public static string FormatMessage(string format, params object[] args)
{return string.Format(format, args);
}// 调用
string message = FormatMessage("Name: {0}, Age: {1}", "Alice", 30);

注意事项

  • params参数本质是数组,方法内部按数组处理
  • 避免在高性能场景中过度使用(数组分配有性能开销)
  • 不能与ref或out同时使用

9. C#中的访问修饰符有哪些?各自的作用范围是什么?

C#提供5种访问修饰符,用于控制类型和成员的访问权限:

  1. public(公共)

    • 完全公开,任何地方都可访问
    • 适用:需要被外部广泛访问的成员
  2. private(私有)

    • 仅在当前类或结构体内部可访问
    • 适用:类内部的实现细节,默认访问级别(接口和枚举除外)
  3. protected(保护)

    • 在当前类及派生类中可访问
    • 适用:需要被继承类使用的成员
  4. internal(内部)

    • 仅在当前程序集(项目)内可访问
    • 适用:同一程序集内共享的类型或成员
  5. protected internal(保护内部)

    • 在当前程序集内或派生类中可访问(取protected和internal的并集)
    • 适用:需要在程序集内或继承体系中共享的成员

示例代码

public class MyBaseClass
{public int PublicField;         // 任何地方可访问private int PrivateField;       // 仅MyBaseClass内部可访问protected int ProtectedField;   // MyBaseClass及派生类可访问internal int InternalField;     // 同一程序集内可访问protected internal int ProtectedInternalField;  // 同一程序集或派生类可访问public void PublicMethod(){// 类内部可访问所有成员PrivateField = 1;ProtectedField = 2;}
}public class MyDerivedClass : MyBaseClass
{public void AccessBaseMembers(){PublicField = 1;            // 允许// PrivateField = 2;        // 错误:无法访问私有成员ProtectedField = 3;         // 允许(派生类)// InternalField = 4;       // 允许仅当在同一程序集ProtectedInternalField = 5; // 允许(派生类)}
}public class ExternalClass
{public void AccessMembers(MyBaseClass obj){obj.PublicField = 1;        // 允许// obj.PrivateField = 2;    // 错误// obj.ProtectedField = 3;  // 错误// obj.InternalField = 4;   // 允许仅当在同一程序集// obj.ProtectedInternalField = 5; // 允许仅当在同一程序集}
}

最佳实践

  • 遵循"最小权限原则",尽量使用更严格的访问级别
  • 类的字段通常设为private,通过public属性暴露
  • 跨程序集共享的类型设为public,内部使用的设为internal

10. 简述static关键字的用法(静态类、静态方法、静态变量)

static关键字:用于声明属于类型本身而非实例的成员,主要有三种用法:

  1. 静态变量(静态字段)

    • 属于类本身,所有实例共享同一变量
    • 内存中只存在一份,在第一次访问类时初始化
    • 常用于存储全局状态或计数器
  2. 静态方法

    • 属于类本身,无需创建实例即可调用
    • 只能访问静态成员,不能访问实例成员
    • 常用于工具类方法、工厂方法等
  3. 静态类

    • 只包含静态成员,不能实例化
    • 不能被继承
    • 常用于工具类(如Math、Convert)

示例代码

// 静态类示例
public static class MathUtility
{// 静态常量public const double Pi = 3.14159;// 静态方法public static double CalculateArea(double radius){return Pi * radius * radius;}
}// 包含静态成员的普通类
public class Counter
{// 静态变量(所有实例共享)private static int _instanceCount = 0;// 实例变量(每个实例独有)private int _id;// 静态构造函数(初始化静态成员)static Counter(){Console.WriteLine("静态构造函数调用");}// 实例构造函数public Counter(){_instanceCount++;_id = _instanceCount;}// 静态方法public static int GetTotalCount(){return _instanceCount;}// 实例方法public int GetId(){return _id;}
}// 使用示例
public static void Main()
{// 调用静态类方法double area = MathUtility.CalculateArea(5);// 使用包含静态成员的类Counter c1 = new Counter();Counter c2 = new Counter();Console.WriteLine(Counter.GetTotalCount());  // 输出 2Console.WriteLine(c1.GetId());  // 输出 1Console.WriteLine(c2.GetId());  // 输出 2
}

静态构造函数特点

  • 无参数,不能有访问修饰符
  • 自动调用,仅调用一次
  • 用于初始化静态成员

注意事项

  • 静态成员生命周期与应用程序相同,可能导致内存占用问题
  • 过度使用静态成员会增加代码耦合度,不利于测试
  • 静态方法不能被重写,但可以被隐藏(使用new关键字)

二、120道C#面试题目录列表

文章序号C#面试题120道
1C#面试题及详细答案120道(01-10)
2C#面试题及详细答案120道(11-20)
3C#面试题及详细答案120道(21-30)
4C#面试题及详细答案120道(31-40)
5C#面试题及详细答案120道(41-50)
6C#面试题及详细答案120道(51-60)
7C#面试题及详细答案120道(61-75)
8C#面试题及详细答案120道(76-85)
9C#面试题及详细答案120道(86-95)
10C#面试题及详细答案120道(96-105)
11C#面试题及详细答案120道(106-115)
12C#面试题及详细答案120道(116-120)
http://www.dtcms.com/a/331534.html

相关文章:

  • 【Android】RecyclerView复用CheckBox的异常状态
  • 容器方式安装Prometheus以及Grafana
  • 《疯狂Java讲义(第3版)》学习笔记ch4
  • C# 贪吃蛇游戏
  • js加密逆向
  • Chrome插件开发实战:从零开发高效Chrome插件,提升浏览器生产力
  • 通过 USB 配置闭环驱动器——易格斯igus
  • glTF-教程/glb-教程
  • tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
  • AI硬件小众赛道崛起:垂直场景的价值重构与增长密码。
  • Java高级流
  • 公链开发竞争白热化:如何设计下一代高性能、可扩展的区块链基础设施?
  • 云手机的存储功能怎么样?
  • 一次 Unity ↔ Android 基于 RSA‑OAEP 的互通踩坑记
  • Android ADB 常用指令全解析
  • ADB服务端调试
  • markdown格式中table表格不生效,没有编译的原因
  • Mybatis Plus 分页插件报错`GOLDILOCKS`
  • 视频号主页的企业信息如何设置?
  • 深入了解linux系统—— 线程概念
  • Fiddler抓包
  • nginx --ssl证书生成mkcert
  • PCB爆板产生的原因有哪些?如何预防?
  • 第三十一天(系统io)
  • Qwen2-VL-2B 轻量化部署实战:数据集构建、LoRA微调、GPTQ量化与vLLM加速
  • 归并排序专栏
  • 机器学习基础讲解
  • Java -- HashSet的全面说明-Map接口的常用方法-遍历方法
  • feed-forward系列工作集合与跟进(vggt以后)
  • 第二十三天:求逆序对