C#中static关键字的完整指南
static关键字是C#中非常重要的概念,用于创建属于类型本身而不是类型实例的成员。
一、static是什么
static是一个声明"静态类"或"静态成员"的关键字。除了类和类成员,在其他任何地方不能使用static。
二、静态类
2.1 静态类是什么
静态类是一个不能被实例化且只能包含静态成员的密封类。
"密封"意味着静态类不能被继承,就像被"封住"了一样,其他类不能以它为父类。
2.2 如何创建静态类
加上 static
的成员不再属于某个对象实例,而是直接属于类本身; 无论创建多少个实例,静态成员只有一份,通过类名即可访问。
示例:
// 创建一个数学工具静态类
public static class MathHelper
{
// 静态字段
public static readonly double PI = 3.14159;
// 静态属性
public static string Version { get; set; } = "1.0.0";
// 静态方法
public static double CalculateCircleArea(double radius)
{
return PI * radius * radius;
}
public static int Add(int a, int b)
{
return a + b;
}
}
示例
using System;
// 静态类定义
public static class MathUtilities
{
// 静态字段
public static readonly double PI = 3.14159;
// 静态方法
public static double CalculateCircleArea(double radius)
{
return PI * radius * radius;
}
public static int Add(int a, int b)
{
return a + b;
}
public static bool IsEven(int number)
{
return number % 2 == 0;
}
}
class Program
{
static void Main()
{
// 直接通过类名访问静态成员
double area = MathUtilities.CalculateCircleArea(5.0);
int sum = MathUtilities.Add(10, 20);
bool isEven = MathUtilities.IsEven(15);
Console.WriteLine($"圆面积: {area}");
Console.WriteLine($"和: {sum}");
Console.WriteLine($"15是偶数: {isEven}");
// 编译错误:不能创建静态类的实例
// MathUtilities utils = new MathUtilities(); // 错误!
}
}
2.3 如何使用静态类
示例:
class Program
{
static void Main()
{
// 直接通过类名使用静态成员,无需创建实例
double area = MathHelper.CalculateCircleArea(5.0);
Console.WriteLine($"圆的面积: {area}");
int sum = MathHelper.Add(10, 20);
Console.WriteLine($"两数之和: {sum}");
Console.WriteLine($"PI值: {MathHelper.PI}");
Console.WriteLine($"工具版本: {MathHelper.Version}");
// ❌ 错误!不能创建静态类的实例
// MathHelper helper = new MathHelper(); // 编译错误
}
}
2.4 静态类的特点
不能实例化:
new StaticClass()
会导致编译错误只能包含静态成员:不能有实例方法、实例字段等
是密封的:不能被继承
不能包含实例构造函数:只能有静态构造函数
通过类名直接访问:不需要创建对象
2.5 何时使用静态类
工具类(如数学计算、字符串处理)
扩展方法容器
全局配置管理
不需要维护状态的工具函数
三、静态成员
3.1. 静态成员是什么
错误理解:静态成员就是静态类的成员。 原因:非静态类可以同时包含静态成员和实例成员,实际上,这是非常常见的用法。它们各自有不同的用途和访问方式。
定义:静态成员是属于类本身而不是类实例的成员。(属于类不属于实例的成员)
这句话包含了静态成员的四个核心特征:
属于类本身(不是对象实例)
通过类名直接访问(不需要创建对象)
在程序运行期间,static成员只有一份副本(所有对象共享)
生命周期与应用程序相同(从类加载到程序结束)
static成员在类型第一次被使用时初始化
3.2 静态字段(Static Fields)
静态字段的特点:
所有实例共享同一个字段
在类第一次被使用时初始化
语法
//标准写法 - 最常用
public static int myField;
private static int myField;
//修饰符顺序调换 - 语法正确但不推荐
static public int myField;
示例1:静态字段 - 学生计数器
public class Student
{
// 普通变量 - 每个学生对象都有自己的
public string Name;
public int Age;
// 静态变量 - 所有学生共享一个计数器
public static int StudentCount = 0;
public Student(string name, int age)
{
this.Name = name;
this.Age = age;
StudentCount++; // 每创建一个学生,计数器加1
}
}
class Program
{
static void Main()
{
Console.WriteLine($"初始学生数: {Student.StudentCount}"); // 0
Student s1 = new Student("小明", 18);
Student s2 = new Student("小红", 17);
// 通过类名访问静态变量(推荐)
Console.WriteLine($"当前学生数: {Student.StudentCount}"); // 2
// 也可以通过对象访问(不推荐)
Console.WriteLine($"通过s1访问: {s1.StudentCount}"); // 2
}
}
示例2:静态字段 计数器
using System;
public class Counter
{
// 实例字段 - 每个对象有自己的副本
public int InstanceCount = 0;
// 静态字段 - 所有对象共享同一个副本
public static int StaticCount = 0;
public Counter()
{
InstanceCount++;
StaticCount++;
}
public void DisplayCounts()
{
Console.WriteLine($"实例计数: {InstanceCount}, 静态计数: {StaticCount}");
}
}
class Program
{
static void Main()
{
Counter c1 = new Counter();
c1.DisplayCounts(); // 实例计数: 1, 静态计数: 1
Counter c2 = new Counter();
c2.DisplayCounts(); // 实例计数: 1, 静态计数: 2
Counter c3 = new Counter();
c3.DisplayCounts(); // 实例计数: 1, 静态计数: 3
// 直接访问静态字段
Console.WriteLine($"总计数器: {Counter.StaticCount}"); // 输出: 3
}
}
3.3 静态属性(Static Properties)
静态属性的特点
属于类级别:通过
ClassName.PropertyName
访问共享存储:所有实例访问的是同一个属性
无需实例化:不需要创建对象即可使用
生命周期长:从程序开始到结束都存在
可包含各种类型:支持只读、读写、自动实现等
线程安全考虑:多线程环境下需要特殊处理
示例:静态属性 完整
using System;
using System.Collections.Generic;
public class AppSettings
{
// 静态属性
public static string AppName { get; set; } = "MyApplication";
public static string Version { get; } = "1.0.0"; // 只读静态属性
// 带有私有字段的静态属性
private static int _userCount = 0;
public static int UserCount
{
get { return _userCount; }
private set { _userCount = value; } // 私有setter
}
public static void AddUser()
{
UserCount++;
}
}
class Program
{
static void Main()
{
// 访问静态属性
Console.WriteLine($"应用名: {AppSettings.AppName}");
Console.WriteLine($"版本: {AppSettings.Version}");
Console.WriteLine($"用户数: {AppSettings.UserCount}");
AppSettings.AddUser();
AppSettings.AddUser();
Console.WriteLine($"更新后用户数: {AppSettings.UserCount}");
// 修改静态属性
AppSettings.AppName = "AwesomeApp";
Console.WriteLine($"新应用名: {AppSettings.AppName}");
}
}
示例:静态属性 基本特点
public class Counter
{
private static int _totalCount; // 静态字段
// 静态属性
public static int TotalCount
{
get { return _totalCount; }
set { _totalCount = value; }
}
// 自动实现的静态属性
public static string AppName { get; set; } = "MyApp";
public int InstanceCount { get; set; } // 实例属性
public Counter()
{
_totalCount++; // 所有对象共享同一个_totalCount
InstanceCount++; // 每个对象有自己的InstanceCount
}
}
示例:静态属性 访问方式
public class Counter
{
private static int _totalCount; // 静态字段
// 静态属性
public static int TotalCount
{
get { return _totalCount; }
set { _totalCount = value; }
}
// 自动实现的静态属性
public static string AppName { get; set; } = "MyApp";
public int InstanceCount { get; set; } // 实例属性
public Counter()
{
_totalCount++; // 所有对象共享同一个_totalCount
InstanceCount++; // 每个对象有自己的InstanceCount
}
}
示例:静态属性 内存共享
public class BankAccount
{
private static double _interestRate = 0.03; // 所有账户共享的利率
// 静态属性 - 所有BankAccount实例共享
public static double InterestRate
{
get { return _interestRate; }
set { _interestRate = value; }
}
public string AccountNumber { get; set; }
public double Balance { get; set; }
public BankAccount(string accountNumber, double balance)
{
AccountNumber = accountNumber;
Balance = balance;
}
public void ApplyInterest()
{
Balance += Balance * InterestRate; // 使用静态属性
}
}
// 使用示例
BankAccount account1 = new BankAccount("001", 1000);
BankAccount account2 = new BankAccount("002", 2000);
Console.WriteLine($"初始利率: {BankAccount.InterestRate}"); // 0.03
// 修改静态属性 - 影响所有账户
BankAccount.InterestRate = 0.05;
account1.ApplyInterest(); // 使用新利率 5%
account2.ApplyInterest(); // 使用新利率 5%
3.4 静态方法(Static Methods)
静态方法的特点:
属于类本身,不属于任何实例
只能访问静态成员,不能直接访问实例成员
不需要创建对象即可调用
问题:static方法可以直接访问类的非静态成员。这句话是否正确?
错误。静态方法属于类级别,在类加载时就可以使用,而非静态成员属于对象级别,需要先创建对象实例才能访问。由于静态方法调用时可能还没有任何对象被创建,因此无法直接访问非静态成员。
public class Example
{
// 实例字段(非静态成员)
private int instanceField = 10;
// 实例方法(非静态成员)
public void InstanceMethod()
{
Console.WriteLine("实例方法");
}
// 静态字段(静态成员)
private static int staticField = 20;
// 静态方法
public static void StaticMethod()
{
// ✅ 可以访问静态成员
Console.WriteLine(staticField);
StaticMethod2();
// ❌ 错误:不能直接访问实例成员
// Console.WriteLine(instanceField); // 编译错误
// InstanceMethod(); // 编译错误
}
public static void StaticMethod2()
{
Console.WriteLine("另一个静态方法");
}
}
问题:静态方法中,可以访问非静态的局部变量。这句话是否正确?
正确。这里的关键是区分类的成员和方法的局部变量:
类的成员:字段、属性、方法等(分为静态成员和实例成员)
局部变量:在方法内部声明的变量(没有静态概念)
public class Example
{
// 实例字段 - 类的成员
private int instanceField = 10;
// 静态字段 - 类的成员
private static int staticField = 20;
// 静态方法
public static void StaticMethod()
{
// ✅ 正确:可以在静态方法中声明和访问局部变量
int localVariable = 5; // 局部变量
string message = "Hello World"; // 局部变量
Console.WriteLine(localVariable); // 输出: 5
Console.WriteLine(message); // 输出: Hello World
// 修改局部变量
localVariable = localVariable + 10;
Console.WriteLine(localVariable); // 输出: 15
// ✅ 正确:局部变量可以引用对象
List<string> names = new List<string> { "Alice", "Bob" };
DateTime now = DateTime.Now;
// ❌ 错误:不能直接访问实例字段
// Console.WriteLine(instanceField); // 编译错误
// ✅ 正确:可以访问静态字段
Console.WriteLine(staticField); // 输出: 20
}
// 实例方法
public void InstanceMethod()
{
// 实例方法中也可以有局部变量
int localVar = 100;
Console.WriteLine(localVar);
// ✅ 实例方法可以访问实例字段
Console.WriteLine(instanceField); // 输出: 10
}
}
示例2:静态方法 - 工具方法
public class Student
{
public string Name;
public int Age;
public static int StudentCount = 0;
public Student(string name, int age)
{
this.Name = name;
this.Age = age;
StudentCount++;
}
// 静态方法 - 可以直接通过类名调用
public static bool IsValidAge(int age)
{
return age >= 0 && age <= 150;
}
// 普通方法 - 需要创建对象才能调用
public void DisplayInfo()
{
Console.WriteLine($"姓名: {Name}, 年龄: {Age}");
}
}
class Program
{
static void Main()
{
// 直接调用静态方法,不需要创建对象
Console.WriteLine($"18岁是否有效: {Student.IsValidAge(18)}"); // True
Console.WriteLine($"200岁是否有效: {Student.IsValidAge(200)}"); // False
// 普通方法需要创建对象
Student s1 = new Student("小明", 18);
s1.DisplayInfo();
// 错误!不能通过类名调用普通方法
// Student.DisplayInfo(); // 编译错误
}
}
示例2:静态方法
using System;
public class StringHelper
{
// 实例方法 - 需要对象实例
public string InstanceToUpper(string input)
{
return input.ToUpper();
}
// 静态方法 - 直接通过类名调用
public static string StaticToUpper(string input)
{
return input.ToUpper();
}
// 静态方法只能访问静态成员
private static string _prefix = "STATIC: ";
public static string FormatMessage(string message)
{
return _prefix + message.ToUpper();
// 编译错误:不能访问实例成员
// return InstanceToUpper(message); // 错误!
}
}
class Program
{
static void Main()
{
// 调用静态方法 - 不需要创建对象
string result1 = StringHelper.StaticToUpper("hello");
string result2 = StringHelper.FormatMessage("world");
Console.WriteLine(result1); // 输出: HELLO
Console.WriteLine(result2); // 输出: STATIC: WORLD
// 调用实例方法 - 需要创建对象
StringHelper helper = new StringHelper();
string result3 = helper.InstanceToUpper("hello");
Console.WriteLine(result3); // 输出: HELLO
}
}
3.5 静态构造函数(Static Constructors)
静态构造函数的特点:
在类第一次被使用前自动调用
只能有一个静态构造函数
不能有访问修饰符
不能有参数(静态构造函数由CLR(公共语言运行时)自动调用,程序员无法控制何时调用静态构造函数,调用时机:在类第一次被使用之前)
问题:实例构造函数可以对静态成员和非静态成员进行初始化,这样理解正确吗
正确。实例构造函数也可以访问和修改静态成员(因为静态成员属于类,对所有实例可见)
using System;
public class DatabaseManager
{
// 静态字段
private static readonly string ConnectionString;
private static readonly DateTime InitializeTime;
// 静态构造函数
static DatabaseManager()
{
InitializeTime = DateTime.Now;
ConnectionString = "Server=localhost;Database=MyApp;Trusted_Connection=true;";
Console.WriteLine($"静态构造函数被调用,初始化时间: {InitializeTime}");
Console.WriteLine("数据库连接已配置");
}
// 静态方法
public static void ExecuteQuery(string query)
{
Console.WriteLine($"执行查询: {query}");
Console.WriteLine($"使用连接字符串: {ConnectionString}");
}
public static void ShowInfo()
{
Console.WriteLine($"初始化时间: {InitializeTime}");
}
}
class Program
{
static void Main()
{
Console.WriteLine("程序开始");
// 第一次使用类,触发静态构造函数
DatabaseManager.ExecuteQuery("SELECT * FROM Users");
// 不会再次调用静态构造函数
DatabaseManager.ShowInfo();
}
}
3.6 静态只读字段(Static Readonly Fields)
using System;
public class Constants
{
// 编译时常量
public const double PI = 3.14159;
// 运行时常量(静态只读字段)
public static readonly DateTime BuildTime;
public static readonly string Version;
// 静态构造函数中初始化
static Constants()
{
BuildTime = DateTime.Now;
Version = "2.1.0";
}
// 复杂类型的静态只读字段
public static readonly List<string> SupportedLanguages = new List<string>
{
"C#", "Java", "Python", "JavaScript"
};
}
class Program
{
static void Main()
{
Console.WriteLine($"PI: {Constants.PI}");
Console.WriteLine($"构建时间: {Constants.BuildTime}");
Console.WriteLine($"版本: {Constants.Version}");
Console.WriteLine("支持的语言:");
foreach (string language in Constants.SupportedLanguages)
{
Console.WriteLine($" - {language}");
}
// 可以修改集合内容,但不能重新赋值
Constants.SupportedLanguages.Add("Go");
// Constants.SupportedLanguages = new List<string>(); // 错误!
}
}
总结
static关键字在C#中用于创建属于类型本身的成员,具有以下特点:
静态类:不能实例化,只包含静态成员
静态成员:所有实例共享,通过类名访问
静态构造函数:在类第一次使用时自动调用
适用场景:工具类、单例模式、常量定义、共享数据
通过合理使用static关键字,可以创建更加高效和组织的代码结构。记住要适度使用,避免过度依赖静态成员导致代码难以测试和维护。