C#基础04-基础语法
1、数据类型
(1)值类型
- 直接存储数据,分配在栈内存中,赋值时复制完整数据。
- 简单类型
- 整数:
byte
(0-255)、sbyte
(-128-127)、short
、ushort
、int
(默认)、uint
、long
、ulong
- 浮点数:
float
(单精度,后缀 F
,如 3.14F
)double
(双精度,默认小数类型)
- 高精度小数:
decimal
(财务计算,后缀 M
,如 10.99M
) - 布尔型:
bool
(仅 true
/false
) - 字符型:
char
(单个Unicode字符,如 'A'
)
- 复合类型
- 枚举(Enum):自定义常量集合,如
enum Days { Mon, Tue }
- 结构体(Struct):轻量级数据封装(如坐标点),不可继承,适合小数据块。
(2)引用类型
- 存储数据的内存地址,分配在堆内存中,赋值时复制引用而非数据本身。
- 内置类型
- 字符串(string):不可变字符序列(如
"Hello"
),支持逐字字符串(@"C:\path"
)。 - 对象(object):所有类型基类,可装箱值类型(如
object obj = 42;
)。 - 动态类型(dynamic):绕过编译时检查,运行时解析类型。
- 自定义类型
- 类(Class):支持继承、构造函数,如
class Person { }
。 - 接口(Interface):定义契约,无实现(如
interface ILogger { void Log(); }
)。 - 数组(Array):同类型元素集合(如
int[] arr = new int[5];
)。 - 委托(Delegate):方法引用(如
Action<int> print = Console.WriteLine;
)。
️(3)特殊类型
- 可空类型(Nullable)
- 值类型扩展(如
int? num = null;
),避免值类型无法为 null
的限制。
- 指针类型(Pointer)
- 仅用于
unsafe
上下文,直接操作内存(如 int* ptr;
)。
(4)类型转换
- 转换方式
- 隐式转换:小范围转大范围(如
int
→ long
)。 - 显式转换:强制类型转换(如
(int)3.14
)。 - 方法转换:
Parse()
、Convert.ToInt32()
等。
(5)类型选择

2、装箱与拆箱
(1)基本概念与原理
- 定义
- 装箱:将值类型(如
int
、struct
)转换为引用类型(如 object
或接口类型)。
int i = 123;
object o = i;
- 拆箱:将引用类型显式转换回原始值类型。
int j = (int)o;
- 本质:值类型存储在栈上,引用类型在托管堆分配内存。装箱时在堆中创建对象副本并返回其引用;拆箱时从堆中复制值回栈。
操作 | 步骤 |
---|
装箱 | 1. 在堆分配内存(值类型大小 + 方法表指针 + SyncBlockIndex) 2. 复制值类型数据到堆 3. 返回对象引用地址 |
拆箱 | 1. 检查对象是否为对应值类型的装箱实例 2. 获取堆中值类型字段地址 3. 复制值到栈上的变量 |
(2)性能影响与常见场景
- 性能损耗根源
- 每次装箱/拆箱均涉及堆内存分配和数据复制,尤其在循环或高频调用中显著降低效率。
- 示例:4000万次装箱耗时约1.4秒,而泛型集合几乎无开销。
- 典型触发场景
- 值类型存入非泛型集合(如
ArrayList
)时自动装箱:
ArrayList list = new ArrayList();
list.Add(42);
- 传递给 `object` 类型参数的方法:
void Process(object obj) { ... }
Process(10);
- 值类型转换为接口类型(如 `ICloneable`)。
(3)优化策略
- 优先使用泛型集合:用
List<T>
替代 ArrayList
避免装箱:
List<int> genericList = new List<int>();
genericList.Add(42);
- 方法重载替代 object 参数:为值类型设计专用重载方法:
void Process(int value) { ... }
void Process(object obj) { ... }
- 谨慎调用继承方法
- 调用重写的
ToString()
不触发装箱(编译器直接调用)。 - 调用
GetType()
或未重写的基类方法(如 System.ValueType
方法)会强制装箱。
- 避免接口转换装箱:值类型实现接口时,转换为接口类型将装箱:
ICloneable c = (ICloneable)a;
(4)易错点与陷阱
object o = 10;
long num = (long)o;
int num = (int)o;
- 装箱后修改不影响原始值:装箱创建的是副本,修改原值不影响装箱对象:
int a = 5;
object o = a;
a = 10;
Console.WriteLine(o);
- 可变值类型(struct)的陷阱:已装箱的结构体无法直接修改字段,需拆箱→修改→重新装箱:
Point p = new Point(1, 1);
object o = p;
p.X = 2;
((Point)o).X = 2;
(5)总结
关键点 | 行动建议 |
---|
避免非泛型集合 | 使用 List<T> /Dictionary<T> 替代非泛型容器 |
警惕接口转换 | 减少值类型向接口类型的隐式转换 |
方法设计 | 为值类型提供重载方法 |
类型安全检查 | 拆箱前用 is 或 as 验证类型 |
3、变量
(1)定义与赋值
数据类型 变量名;
数据类型 变量名 = 值;
int age;
string name = "Alice";
double price = 9.99;
int a = 5, b = 10, c;
(2)类型与内存管理
- 值类型变量
- 直接存储数据,内存分配在栈上
- 包括整数 (
int
)、浮点数 (float
)、布尔 (bool
)、结构体 (struct
) 等 - 特点:赋值时复制完整数据副本。
- 引用类型变量
- 存储数据的内存地址,内存分配在堆上
- 包括类 (
class
)、字符串 (string
)、数组 (int[]
)、接口 (interface
) - 特点:赋值时复制引用地址,多个变量可指向同一对象。
- 关键区别:
int x = 10;
int y = x;
int[] arr1 = { 1, 2 };
int[] arr2 = arr1;
arr2[0] = 99;
(3)作用域与生命周期
作用域类型 | 定义位置 | 访问范围 | 示例 |
---|
局部变量 | 方法/代码块内部 | 仅限所在代码块 | void Foo() { int local = 5; } |
成员变量 | 类内部(与方法同级) | 类内所有方法 | class Person { private int age; } |
静态变量 | 类内部 + static 修饰 | 全局可访问(无需实例化) | public static int Count; |
全局变量方案 | 静态类封装 | 跨窗体/类访问 | public static class Global { public static string UserName; } |
- 最佳实践:
- 避免滥用静态变量(常驻内存易导致性能问题)
- 优先使用局部变量,减少命名冲突风险
(4)命名规范与规则
- 强制规则:
- 以字母/下划线开头,后续可包含字母、数字、下划线
- 区分大小写(
myVar
≠ MyVar
) - 禁止使用 C# 关键字(如
int
、class
)
- 行业惯例:
- Camel 命名法(局部变量/参数):首单词小写,后续单词首字母大写 →
userAge
、itemList
- Pascal 命名法(公共成员/类):所有单词首字母大写 →
UserName
、CalculateTotal()
- 拒绝无意义命名(如
a1
, tmp
)
(5)关键注意事项
- 初始化要求:局部变量使用前必须显式赋值,否则编译错误
int num;
Console.WriteLine(num);
const double PI = 3.14;
const int MAX_SIZE;
- 字符串特性:
string
为不可变引用类型,修改会创建新对象
string s1 = "Hello";
s1 += " World";
(6)精选实例场景
Console.Write("请输入年龄:");
string input = Console.ReadLine();
int age = int.Parse(input);
enum Status { Running, Stopped }
Status current = Status.Running;
int? score = null;
if (score.HasValue) Console.WriteLine(score.Value);
4、深拷贝浅拷贝
(1)核心概念对比
特性 | 浅拷贝 | 深拷贝 |
---|
定义 | 仅复制对象的值类型成员和引用类型成员的地址引用(指向同一内存实例) | 递归复制对象的所有层级成员,包括引用类型成员的实际对象(创建全新实例) |
影响范围 | 修改副本的引用类型成员会影响原始对象(共享数据) | 副本与原始对象完全独立,互不影响 |
实现方法 | MemberwiseClone() (Object 类内置方法) | 反射、序列化、表达式树、手动实现 |
适用场景 | 对象无嵌套引用类型或无需隔离引用数据 | 对象含嵌套引用类型且需完全隔离数据 |
(2)浅拷贝实现
public class Person : ICloneable
{public string Name; public Address Address; public object Clone(){return this.MemberwiseClone(); }
}public class Address
{public string City;
}
Person p1 = new Person { Name = "Alice", Address = new Address { City = "Beijing" } };
Person p2 = (Person)p1.Clone();
p2.Address.City = "Shanghai";
Console.WriteLine(p1.Address.City);
(3)深拷贝实现
- 反射:解决循环引用需额外处理,如用字典缓存已拷贝对象避免无限递归。
public static T DeepCopyByReflection<T>(T obj)
{if (obj is string || obj.GetType().IsValueType) return obj;object retval = Activator.CreateInstance(obj.GetType());foreach (FieldInfo field in obj.GetType().GetFields()){object value = field.GetValue(obj);field.SetValue(retval, DeepCopyByReflection(value)); }return (T)retval;
}
- 序列化(推荐方式):序列化法支持复杂对象图,但需确保所有嵌套类可序列化(标记
[Serializable]
特性)。
public static T DeepCopyBySerialization<T>(T obj)
{using (MemoryStream ms = new MemoryStream()){BinaryFormatter formatter = new BinaryFormatter();formatter.Serialize(ms, obj);ms.Seek(0, SeekOrigin.Begin);return (T)formatter.Deserialize(ms);}
}
public static T DeepCopyByJson<T>(T obj)
{return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
- 表达式树(高性能方案):通过动态生成表达式树编译为委托,实现高效深拷贝(适用于频繁拷贝场景)。
(3)典型场景分析
场景 | 推荐拷贝方式 | 原因 |
---|
对象无嵌套引用类型 | 浅拷贝 | 效率最高(MemberwiseClone 是内存级复制) |
嵌套引用类型需完全隔离 | 序列化深拷贝 | 自动处理复杂对象图,代码简洁 |
高频深拷贝且对象结构稳定 | 表达式树 | 编译后接近原生代码性能 |
需精细控制拷贝过程 | 手动实现深拷贝 | 对特定字段定制逻辑(如忽略某些属性) |
(4)实践建议
- 优先序列化法:使用
Newtonsoft.Json
或 System.Text.Json
实现深拷贝,兼顾安全性与可维护性。 - 警惕循环引用:反射实现时需用字典记录已拷贝对象,避免栈溢出。
- 性能权衡:浅拷贝性能最优,深拷贝中表达式树 > 反射 > 序列化(实测序列化比反射慢 10 倍以上)。
- 不可变类型优化:若引用类型为不可变(如
string
),浅拷贝即可满足隔离需求。
5、运算符
(1)分类与功能
类别 | 运算符 | 功能说明 | 示例 |
---|
算术运算符 | + - * / % ++ -- | 数值计算(含自增/减) | int a = (10 + 3) * 2; |
关系运算符 | == != > < >= <= | 值比较(返回bool ) | if (age >= 18) {...} |
逻辑运算符 | && ` | | !` |
位运算符 | & ` | ^` `~` `<<` `>>` | 二进制位操作(异或^ :相同为0,不同为1) |
赋值运算符 | = += -= *= /= &= ` | =` | 赋值与复合赋值 |
特殊运算符 | ?: is as sizeof typeof ?? | 条件/类型检查/空值处理 | string s = input ?? "default"; |
+
可重载用于字符串拼接(如 "Hello" + name
)- 异或运算
^
常用于加密校验(如 a ^ b ^ a = b
) - 条件运算符
?:
是唯一的三元运算符:condition ? val1 : val2
(2)优先级
- 顶级:
()
(括号) .
(成员访问) ++
(前缀自增) --
(前缀自减) !
~
→ 优先级最高,括号强制改变顺序
int r = (a + b) * c;
- 算术运算:
*
/
%
→ +
-
→ 乘除优先于加减
double v = 5 + 3 * 2;
- 位移运算:
<<
>>
→ 处理二进制移位(如 0x01 << 2 = 0x04
) - 关系比较:
>
<
>=
<=
→ ==
!=
→ 大小比较优先于相等性判断 - 逻辑运算:
&
→ ^
→ \|
→ &&
→ \|\|
→ 短路逻辑&&
/\|\|
效率高于位逻辑 - 赋值运算:
=
及复合赋值(如 +=
)
→ 优先级最低,最后执行 - 后缀运算:
++
--
(后缀形式)
int i = 5;
int j = i++;
- 优先级口诀:括号 > 算数 > 移位 > 比较 > 逻辑 > 赋值
️(3)关键注意事项
- 类型兼容性: 算术运算需操作数类型相同(如
int + double
需显式转换)
double r = 5 + 2.0;
- 短路逻辑优化:
&&
左侧为false
时跳过右侧计算,\|\|
左侧为true
时同理 - 空值处理: 空合并运算符
??
避免NullReferenceException
:
string name = input ?? "Unknown";
- 运算符重载:支持自定义类型重载
+
、==
等(需public static
方法)
(4)调试与最佳实践
bool valid = (a > b) && (c != d) || (e == f);
bool cond1 = a > b;
bool cond2 = c != d;
bool cond3 = e == f;
bool valid = cond1 && cond2 || cond3;
- 避免优先级混淆:多用括号明确意图(如
(a \| b) && c
区别于 a \| (b && c)
)
6、相等Equals
(1)Equals
方法的默认行为
- 引用类型
- 默认比较对象的引用地址(即是否为同一实例)。
- 示例:
var obj1 = new object();
var obj2 = new object();
Console.WriteLine(obj1.Equals(obj2));
- 值类型
- 默认通过
ValueType.Equals
比较所有字段的值是否相等(使用反射,性能较低)。 - 建议重写以提升效率。
(2)重写 Equals
的规范步骤
public override bool Equals(object obj)
{if (obj == null) return false;if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((MyClass)obj);
}
- 实现
IEquatable<T>
接口(避免装箱)
public bool Equals(MyClass other)
{return this.Field1 == other.Field1 && this.Field2.Equals(other.Field2);
}
- 重写
GetHashCode()
:必须保证若两对象 Equals
返回 true
,则其哈希码必须相同
public override int GetHashCode()
{return (Field1, Field2).GetHashCode();
}
public static bool operator ==(MyClass a, MyClass b) => Equals(a, b);
public static bool operator !=(MyClass a, MyClass b) => !Equals(a, b);
(3)其他相等性判断方法
方法 | 适用场景 | 示例 |
---|
ReferenceEquals(a, b) | 强制比较引用地址(值类型永远返回 false ) | ReferenceEquals("a", "a") → false |
== 运算符 | 默认行为同 Equals ,但可被重载(如 string 比较值) | "a" == "a" → true |
- 特殊类型注意:
string
:Equals
和 ==
均比较内容而非引用。 - 忽略大小写比较字符串:
"HELLO".Equals("hello", StringComparison.OrdinalIgnoreCase);
(4)必须遵守的相等性原则
- 自反性:
x.Equals(x)
为 true
。 - 对称性:若
x.Equals(y)
为 true
,则 y.Equals(x)
必须为 true
。 - 传递性:若
x.Equals(y)
和 y.Equals(z)
为 true
,则 x.Equals(z)
必须为 true
。 - 一致性:多次调用结果不变。
(5)常见误区
- 值类型使用
ReferenceEquals
:因装箱导致结果恒为 false
。 - 未同步重写
GetHashCode
:导致哈希集合(如 Dictionary
)行为异常。 - 混淆
==
与 Equals
:引用类型未重载时二者等效,但值类型 ==
需显式重载。
(6)最佳实践
- 值类型:务必重写
Equals
和 GetHashCode
,避免反射开销。 - 引用类型:若需值语义比较,完整实现
IEquatable<T>
+ 运算符重载。 - 字符串比较:明确指定
StringComparison
参数以避免区域性差异。
7、关键字
(1)基础声明与类型定义
关键字 | 功能说明 | 示例 |
---|
class | 定义引用类型,封装数据和行为 | class Person { public string Name; } |
struct | 定义值类型,适用于轻量数据结构(≤16字节) | struct Point { public int X, Y; } |
enum | 声明枚举类型(固定值集合) | enum Status { Running, Stopped } |
delegate | 定义方法签名,用于事件和回调 | delegate void LogHandler(string message); |
interface | 定义契约(无实现),支持多继承 | interface IDrawable { void Draw(); } |
(2)流程控制与逻辑处理
if (age > 18) Console.WriteLine("Adult");
- `switch`/`case`:多路分支
switch (status) { case Status.Running: break; }
- `for`/`foreach`:迭代集合
foreach (var item in list) { ... }
- `break`/`continue`:循环控制 * `break` 终止当前循环,`continue` 跳过本次迭代
- 异常处理
try
/catch
/finally
:捕获异常并清理资源
try { ... } catch (Exception ex) { ... } finally { ... }
- `throw`:主动抛出异常
(3)面向对象核心特性
关键字 | 用途 | 示例 | 规则 |
---|
virtual | 声明可重写方法 | public virtual void Calculate() { ... } | 派生类用 override 重写 |
override | 重写基类虚方法 | public override void Calculate() { ... } | 方法签名必须一致 |
abstract | 定义抽象成员(无实现),强制派生类实现 | public abstract void Render(); | 只能在抽象类中使用 |
sealed | 禁止类被继承或方法被重写 | sealed class AdminUser : User { } | 常与 override 联用 |
base | 调用基类构造方法或成员 | public Child() : base() { } | 必须在构造函数首行 |
this | 引用当前实例成员 | this.name = name; | 区分同名局部变量 |
(4)类型操作与安全
Shape s = circle as Shape;
- `is`:类型检查
if (obj is string) { ... }
- `typeof`:获取类型元数据
Type t = typeof(int);
string name = input ?? "Unknown";
class Repository<T> where T : IDisposable { }
(5)特殊功能关键字
关键字 | 用途 | 场景示例 | 注意 |
---|
using | 引入命名空间或自动释放资源 | using (var file = new StreamReader(...)) { ... } | 实现 IDisposable 的对象 |
unsafe | 启用指针操作(需项目配置) | unsafe { int* p = # } | 仅限信任环境 |
fixed | 固定变量内存地址(防GC回收) | fixed (int* ptr = arr) { ... } | 在 unsafe 块内使用 |
volatile | 确保多线程下字段可见性 | private volatile bool _isRunning; | 避免编译器优化 |
const | 定义编译时常量 | const double PI = 3.14; | 必须初始化且不可修改 |
readonly | 定义运行时常量(构造函数内可赋值) | public readonly string Id = Guid.NewGuid().ToString(); | 比 const 更灵活 |
(6)上下文关键字
var list = new List<int>();
public string Name { set { _name = value; } }
IEnumerable<int> GetNumbers() { yield return 1; }
async Task LoadData() { await httpClient.GetAsync(...); }
(7)关键对比速查表
场景 | 推荐关键字 | 替代方案 | 选择依据 |
---|
方法返回值 | void | - | 无返回值时必用 |
跨方法返回多个值 | out /ref | 元组/自定义对象 | 简单场景用 out ,需双向修改用 ref |
常量定义 | const | readonly | 编译时确定用 const ,运行时用 readonly |
禁止继承 | sealed | 私有构造函数 | 需完全封闭类时 |
异步操作 | async /await | 回调/ThreadPool | 简化异步代码流 |
8、注释
(1)基础类型与语法
int age = 30;
- 多行注释 (
/* ... */
) :注释代码块或详细说明复杂逻辑
double ApplyDiscount(double price, bool isMember) {...}
- XML文档注释 (
///
) :生成API文档,兼容IDE智能提示
int Add(int a, int b) => a + b;
(2)核心作用与场景
- 代码解释
- 说明复杂算法意图(如排序逻辑)
- 标注关键参数含义(避免歧义)
- 提升代码可维护性
- 调试辅助
- 临时禁用代码段(
/* ... */
包裹) - 标记待优化区域(
// TODO: 重构此方法
)
- 文档生成
- XML注释可通过工具(如Sandcastle)生成HTML/CHM文档
(3)高级技巧与最佳实践
#if DEBUG
Console.WriteLine("调试模式输出");
#endif
- 注释规范建议
- 简洁性:避免冗余(如
i++; // i自增
) - 实时性:代码修改后同步更新注释
- 一致性:团队统一注释风格(如所有公共方法用XML注释)
- VS智能应用
- 输入
///
自动生成XML模板(自动填充<param>
等标签) - 折叠注释区域:
#region 算法说明 ... #endregion
(4)注意事项
- 嵌套问题:多行注释不可嵌套(
/* /* 错误示例 */ */
) - 位置规范:
- 特殊字符:XML注释中需转义
<
为<
(5)综合示例
public class AccountManager
{private int _status; public bool Login(string username, string password){if (username.Length < 3) return false;return true;}
}