1. 变量、常量与命名规范概述
在C#编程中,变量和常量是最基础也是最重要的概念之一。它们就像是程序中的"储物柜",用来存放各种数据。而命名规范则是保证代码整洁、易读的关键。作为从C++转C#的老程序员,我发现很多初学者容易忽视这些基础概念的重要性,导致后期维护困难。
变量本质上是一块内存空间的标识符,它的值可以在程序运行过程中改变。而常量则是固定不变的值,一旦定义就不能修改。在C#中,我们使用var关键字进行类型推断,这大大简化了代码编写。但要注意,var并不是"无类型"或"动态类型",编译器会根据赋值表达式自动推断出具体类型。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 变量详解
2.1 变量的声明与初始化
在C#中声明变量需要指定类型和名称,基本语法如下:
csharp复制数据类型 变量名 = 初始值;
例如:
csharp复制int age = 25;
string name = "张三";
double salary = 5000.50;
从C# 3.0开始,我们可以使用var关键字进行隐式类型声明:
csharp复制var age = 25; // 编译器推断为int
var name = "张三"; // 编译器推断为string
var salary = 5000.50; // 编译器推断为double
注意:使用var时必须在声明时初始化,因为编译器需要通过初始值来推断类型。
2.2 变量的作用域
变量的作用域决定了它在代码中的可见范围:
- 块级作用域:在{}内声明的变量,只能在该块内访问
- 方法级作用域:在方法内声明的变量,只能在该方法内访问
- 类级作用域:在类中声明的变量(字段),可以在整个类中访问
示例:
csharp复制class Program
{
static int classLevel = 10; // 类级作用域
static void Main()
{
int methodLevel = 20; // 方法级作用域
if(true)
{
int blockLevel = 30; // 块级作用域
Console.WriteLine(blockLevel);
}
// Console.WriteLine(blockLevel); // 错误:blockLevel在此不可见
}
}
2.3 值类型与引用类型
C#中的变量分为值类型和引用类型:
-
值类型:直接包含数据,存储在栈上
- 基本类型:int, float, double, bool等
- 结构体:struct
- 枚举:enum
-
引用类型:存储对数据的引用,数据本身存储在堆上
- 类:class
- 接口:interface
- 数组
- 委托:delegate
示例:
csharp复制// 值类型
int a = 10;
int b = a; // 复制值
b = 20;
Console.WriteLine(a); // 输出10,a不受b改变影响
// 引用类型
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // 复制引用
arr2[0] = 100;
Console.WriteLine(arr1[0]); // 输出100,arr1和arr2引用同一对象
3. 常量详解
3.1 const常量
const关键字用于定义编译时常量,必须在声明时初始化,且只能是基本类型:
csharp复制const double PI = 3.14159;
const int MaxUsers = 100;
特点:
- 编译时确定值
- 必须能在编译时计算出来
- 只能是基本类型或字符串
- 隐式静态,通过类名访问
3.2 readonly常量
readonly关键字用于定义运行时常量,可以在声明时或构造函数中初始化:
csharp复制class Config
{
public readonly string ConnectionString;
public Config(string connStr)
{
ConnectionString = connStr;
}
}
特点:
- 运行时确定值
- 可以是任意类型
- 实例级别,每个对象可以有不同的值
- 只能在声明时或构造函数中赋值
3.3 const与readonly的选择
选择依据:
- 如果值是真正不变的(如数学常数),使用const
- 如果值需要在运行时确定(如配置文件读取),使用readonly
- 如果是引用类型,只能使用readonly
4. 命名规范
4.1 C#命名约定
-
Pascal命名法:首字母大写,后续每个单词首字母大写
- 用于:类名、方法名、属性名、命名空间
- 示例:
StudentManager,CalculateSalary
-
Camel命名法:首字母小写,后续每个单词首字母大写
- 用于:局部变量、方法参数
- 示例:
studentName,maxCount
-
全大写加下划线
- 用于:常量
- 示例:
MAX_SIZE,DEFAULT_TIMEOUT
4.2 推荐的命名实践
-
变量名:
- 使用名词或名词短语
- 避免单个字母(循环变量除外)
- 示例:
customerList,orderTotal
-
方法名:
- 使用动词或动词短语
- 示例:
GetUserById,CalculateTotalPrice
-
布尔变量/方法:
- 使用is, can, has等前缀
- 示例:
isValid,canExecute
-
集合:
- 使用复数形式
- 示例:
customers,orders
4.3 应避免的命名
-
匈牙利命名法(已过时):
- 错误示例:
strName,iCount
- 错误示例:
-
缩写除非广泛认可:
- 错误示例:
custLst(应为customerList) - 可接受:
id,url
- 错误示例:
-
与关键字冲突:
- 错误示例:
class,int - 解决方案:加@前缀,如
@class
- 错误示例:
5. 常见问题与最佳实践
5.1 var的使用场景
推荐使用var的场景:
-
类型名很长时:
csharp复制var dict = new Dictionary<string, List<int>>(); -
匿名类型时:
csharp复制var person = new { Name = "张三", Age = 25 }; -
LINQ查询结果:
csharp复制var result = from p in people where p.Age > 18 select p;
不推荐使用var的场景:
-
基本类型:
csharp复制var count = 5; // 不推荐,直接写int更清晰 -
类型不明显时:
csharp复制var data = GetData(); // 不推荐,不知道返回类型
5.2 常量使用陷阱
-
const常量跨程序集问题:
- 如果A程序集定义了const常量,B程序集引用它
- 修改A中的常量值后,B必须重新编译才能获取新值
-
readonly vs const性能:
- const有轻微性能优势(编译时替换)
- 但差异通常可以忽略不计
5.3 命名常见错误
-
不一致的大小写:
- 错误示例:在同一代码中混用
customerCount和CustomerCount
- 错误示例:在同一代码中混用
-
无意义的名称:
- 错误示例:
data,value,temp
- 错误示例:
-
误导性名称:
- 错误示例:
GetUsers()方法却返回单个用户
- 错误示例:
5.4 类型推断的边界情况
-
数字字面量:
csharp复制var a = 5; // int var b = 5.0; // double var c = 5m; // decimal var d = 5f; // float -
集合初始化器:
csharp复制var list = new List<int> { 1, 2, 3 }; // List<int> var array = new[] { 1, 2, 3 }; // int[] -
null处理:
csharp复制var x = null; // 错误:无法推断类型 var y = (string)null; // 正确:明确指定类型
6. 实际应用案例
6.1 配置管理中的常量使用
csharp复制public class AppSettings
{
public const string AppName = "MyApplication";
public const int MaxRetryCount = 3;
public readonly string LogPath;
public AppSettings()
{
LogPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
+ "\\MyApp\\Logs";
}
}
6.2 变量命名的实际改进
改进前:
csharp复制public void p(int a, int b)
{
int c = a + b;
Console.WriteLine(c);
}
改进后:
csharp复制public void PrintSum(int firstNumber, int secondNumber)
{
int sum = firstNumber + secondNumber;
Console.WriteLine(sum);
}
6.3 使用var简化LINQ查询
csharp复制var employees = new List<Employee>
{
new Employee { Id = 1, Name = "张三", Department = "IT", Salary = 8000 },
new Employee { Id = 2, Name = "李四", Department = "HR", Salary = 7500 },
new Employee { Id = 3, Name = "王五", Department = "IT", Salary = 9000 }
};
var itDepartmentStats = from e in employees
where e.Department == "IT"
group e by e.Department into g
select new
{
Department = g.Key,
Count = g.Count(),
AvgSalary = g.Average(x => x.Salary)
};
foreach (var stat in itDepartmentStats)
{
Console.WriteLine($"部门:{stat.Department}, 人数:{stat.Count}, 平均工资:{stat.AvgSalary}");
}
7. 高级主题与扩展
7.1 元组与解构中的变量
C# 7.0引入了元组和解构,为变量使用带来了新方式:
csharp复制// 元组
var person = (Name: "张三", Age: 25);
Console.WriteLine($"{person.Name}, {person.Age}");
// 解构
var (name, age) = person;
Console.WriteLine($"{name}, {age}");
// 方法返回多个值
static (int max, int min) FindMinMax(int[] numbers)
{
return (numbers.Max(), numbers.Min());
}
var result = FindMinMax(new[] { 1, 2, 3, 4, 5 });
Console.WriteLine($"Max: {result.max}, Min: {result.min}");
7.2 模式匹配中的变量
C#的模式匹配功能可以更灵活地使用变量:
csharp复制object obj = "这是一个字符串";
if (obj is string s)
{
Console.WriteLine($"字符串长度为: {s.Length}");
}
switch (obj)
{
case int i:
Console.WriteLine($"这是一个整数: {i}");
break;
case string str when str.Length > 5:
Console.WriteLine($"这是一个长字符串: {str}");
break;
case string str:
Console.WriteLine($"这是一个短字符串: {str}");
break;
default:
Console.WriteLine("未知类型");
break;
}
7.3 可空引用类型中的变量
C# 8.0引入的可空引用类型改变了变量声明方式:
csharp复制#nullable enable
string nonNullableString = "Hello"; // 不能为null
string? nullableString = null; // 可以为null
// 使用null包容运算符!
string forcedNonNull = nullableString!;
// 空条件检查
if (nullableString is not null)
{
Console.WriteLine(nullableString.Length);
}
8. 性能考量与优化
8.1 变量作用域与性能
变量的作用域会影响GC的行为:
- 尽量缩小变量作用域
- 避免在循环中重复创建对象
- 对于频繁使用的局部变量,可以考虑提升为字段
示例:
csharp复制// 不推荐:每次循环都创建新对象
for (int i = 0; i < 1000; i++)
{
var processor = new DataProcessor();
processor.Process(i);
}
// 推荐:重用对象
var processor = new DataProcessor();
for (int i = 0; i < 1000; i++)
{
processor.Process(i);
}
8.2 结构体与类的选择
对于小型数据结构,使用结构体可能更高效:
csharp复制// 结构体(值类型)
public struct Point
{
public int X;
public int Y;
}
// 类(引用类型)
public class PointClass
{
public int X;
public int Y;
}
// 使用
var point1 = new Point { X = 10, Y = 20 }; // 栈上分配
var point2 = new PointClass { X = 10, Y = 20 }; // 堆上分配
选择依据:
- 如果类型很小(16字节以内)且不可变,考虑结构体
- 如果需要继承或多态,必须使用类
- 如果需要作为字典键,结构体通常更高效
8.3 字符串处理优化
字符串是不可变的,频繁修改会导致性能问题:
csharp复制// 不推荐:产生多个临时字符串
string result = "";
for (int i = 0; i < 100; i++)
{
result += i.ToString();
}
// 推荐:使用StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 100; i++)
{
sb.Append(i);
}
string finalResult = sb.ToString();
9. 团队协作中的命名规范
9.1 制定团队规范
-
基础规则:
- 所有公共成员使用PascalCase
- 所有私有字段使用_camelCase
- 局部变量使用camelCase
- 常量使用UPPER_CASE
-
前缀后缀约定:
- 接口使用I前缀:
IEnumerable - 异步方法使用Async后缀:
GetDataAsync - 事件使用On前缀:
OnClick
- 接口使用I前缀:
-
项目特定约定:
- UI控件前缀:
btnSubmit,txtName - 特定领域缩写:
CRM代表客户关系管理
- UI控件前缀:
9.2 使用分析工具
-
StyleCop:
- 检查命名规范
- 强制代码风格一致性
- 可自定义规则
-
Roslyn分析器:
- 实时代码分析
- 自定义诊断规则
- 与CI/CD集成
-
EditorConfig:
- 跨编辑器/IDE共享配置
- 定义缩进、换行等风格
- 与Git集成,确保团队一致
9.3 代码审查要点
-
命名审查:
- 名称是否清晰表达意图?
- 是否遵循团队规范?
- 是否有拼写错误?
-
变量使用审查:
- 作用域是否合理?
- 是否有未使用的变量?
- 常量是否被误用为变量?
-
类型选择审查:
- var使用是否恰当?
- 值类型/引用类型选择是否合理?
- 可空性是否正确处理?
10. 现代C#中的变量特性
10.1 顶级语句中的变量
C# 9.0的顶级语句简化了变量声明:
csharp复制// 传统方式
class Program
{
static void Main()
{
string name = "张三";
Console.WriteLine(name);
}
}
// 顶级语句方式
string name = "张三";
Console.WriteLine(name);
10.2 全局using与隐式using
C# 10.0引入的全局using指令影响变量声明:
csharp复制// 传统方式
using System;
class Program
{
static void Main()
{
var list = new List<int>(); // 需要using System.Collections.Generic
}
}
// 全局using方式
global using System;
global using System.Collections.Generic;
var list = new List<int>(); // 现在可以直接使用
10.3 记录(record)类型中的变量
记录类型提供了更简洁的不可变变量声明:
csharp复制// 传统类
public class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
// 记录类型
public record Person(string Name, int Age);
// 使用
var person = new Person("张三", 25);
var (name, age) = person; // 解构
10.4 文件局部类型
C# 11.0的文件局部类型限制变量可见性:
csharp复制file class Helper
{
public static int Calculate(int x) => x * 2;
}
var result = Helper.Calculate(10); // 只能在同一文件中访问
