Administrator
发布于 2025-10-12 / 2 阅读
0
0

static关键字详解

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 静态类的特点

  1. 不能实例化new StaticClass() 会导致编译错误

  2. 只能包含静态成员:不能有实例方法、实例字段等

  3. 是密封的:不能被继承

  4. 不能包含实例构造函数:只能有静态构造函数

  5. 通过类名直接访问:不需要创建对象

2.5 何时使用静态类

  • 工具类(如数学计算、字符串处理)

  • 扩展方法容器

  • 全局配置管理

  • 不需要维护状态的工具函数


三、静态成员

3.1. 静态成员是什么

错误理解:静态成员就是静态类的成员。 原因:非静态类可以同时包含静态成员和实例成员,实际上,这是非常常见的用法。它们各自有不同的用途和访问方式。

定义:静态成员是属于类本身而不是类实例的成员。(属于类不属于实例的成员)

这句话包含了静态成员的四个核心特征:

  1. 属于类本身(不是对象实例)

  2. 通过类名直接访问(不需要创建对象)

  3. 在程序运行期间,static成员只有一份副本(所有对象共享)

  4. 生命周期与应用程序相同(从类加载到程序结束)

  5. 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)

静态属性的特点

  1. 属于类级别:通过 ClassName.PropertyName 访问

  2. 共享存储:所有实例访问的是同一个属性

  3. 无需实例化:不需要创建对象即可使用

  4. 生命周期长:从程序开始到结束都存在

  5. 可包含各种类型:支持只读、读写、自动实现等

  6. 线程安全考虑:多线程环境下需要特殊处理

示例:静态属性 完整

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关键字,可以创建更加高效和组织的代码结构。记住要适度使用,避免过度依赖静态成员导致代码难以测试和维护。


评论