目录

静态类(static class)

1.场景一:全局状态管理

例如: 登录窗体 -> 主窗体 传值

原理:静态类在内存中只有一份,且在整个应用程序生命周期(AppDomain)内一直存在。好比在软件内放了一块“公共黑板”,谁都可以去写,谁都可以去读。

// 定义一个静态类,充当“全局会话”
public static class SessionContext
{
    // 存储当前登录的用户信息
    public static User CurrentUser { get; set; }
    
    // 存储全局配置
    public static string ApiUrl { get; set; } = "http://api.baidu.com";
    
    // 标记系统是否已连接
    public static bool IsConnected { get; set; }
}

// 登录窗体 (LoginWindow)
private void BtnLogin_Click(...)
{
    var user = CheckPassword(txtUser.Text, txtPwd.Text);
    if(user != null)
    {
        // 1. 写黑板
        SessionContext.CurrentUser = user;
        SessionContext.IsConnected = true;
        
        // 2. 跳转
        new MainWindow().Show();
        this.Close();
    }
}

// 主窗体 (MainWindow)
public MainWindow()
{
    InitializeComponent();
    // 3. 读黑板
    lblWelcome.Text = $"欢迎回来,{SessionContext.CurrentUser.Name}";
}
  • 优点:简单,粗暴,有效。对与中小型WPF软件,这是管理"当前用户",“软件配置"最快的方式。
  • 缺点:1.耦合度高:所有模块都依赖整个静态类,想拆分模块或做单元测试异常痛苦。2.线程安全:既然是全局共享,如果多个线程同时改它(比如后台线程改状态,UI线程读状态),不加锁(lock)就会出Bug。

2.场景二:扩展方法(Extension Methods)

静态类在C#中最不可替代的功能。Linq中的 .Where(), .ToList() ,它们全是静态类里的静态方法。

在WPF中,经常用静态类来扩展原生控件的功能,让代码写起来像“原生”的一样

场景:WPF的可视化树(VisulTree)很复杂,找父控件(比如从Button找所在的 ListViewItem) 很麻烦,可以写个扩展方法。

// 必须是静态类
public static class VisualTreeHelperExtensions
{
    // 方法必须是静态的,第一个参数必须加 "this" 关键字
    public static T FindParent<T>(this, DependencyObject child) Where T : DependencyObject
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);
   
        if (parentObject == null)
        {
             return null;
        }

        if (parentObject is T parent)
        {
            return parent;
        }
        else
        {
            return FindParent<T>(parentObject);
        }
    }
}

// 使用效果
private void Button_Click(object sender, RoutedEventArgs e)
{
    Button btn = sender as Button;
    var listViewItem = btn.FindPatent<ListViewItem>();
}

静态类不仅仅是工具库,还是代码语法的增强器。

3.场景三:单例模式的变种(ViewModelLocator)

在WPF的MVVM框架(例如 prism)中,经常会看到一个静态类或者静态属性,用来让 View 找到ViewModel

public class ViewModelLocator
{
    public static MainViewModel Main {get;} = new MainViewModel();
    public static LoginViewModel Login {get;} = new LoginViewModel();
}

App.xaml使用

<Application.Resource>
   <local:ViewModelLoactor x:Key="Locator"/>
</Application.Resource>

MainWindow.xaml 使用:

<!-- 这里的 Source 最终指向了那个静态属性 -->
<Window DataContext="{Binding Main, Source={StaticResource Locator}}">

这里的静态属性充当了“服务定位器”的角色,让 XAML 这种标记语言能够方便地找到 C# 内存里的对象。

场景四:WPF 的转换器 (Converter) 优化

在 XAML 里经常写 BooleanToVisibilityConverter。通常需要在 <Window.Resources> 里实例化它。
但如果是一个静态类单例,使用起来会更性能友好一点(虽然微乎其微,但体现了极客精神)。

// 常见的写法,继承 IValueConverter
public class BoolToVisConverter : IValueConverter
{
    // 提供一个静态的单例,方便 C# 代码里直接用
    public static BoolToVisConverter Instance { get; } = new BoolToVisConverter();

    public object Convert(...) { ... }
}

静态类使用的“三不原则”

虽然静态类好用,但在现代架构设计中,我们极其谨慎地使用它。因为“一旦静态,就很难回头”。

1. 不要在静态类里持有 UI 控件
错误示范

public static class GlobalCache
{
    // 绝对禁止!这就叫内存泄漏!
    // 只要程序不关,这个 Button 永远无法被垃圾回收,
    // 连带着 Button 所在的 Window 也死不掉。
    public static Button LastClickedButton { get; set; } 
}

2. 不要在静态类里写复杂的业务逻辑
如果你把“订单计算”、“数据库事务”全写在静态方法里,你的程序就变成了面向过程编程,而不是面向对象。后期想给订单计算换个算法,或者做模拟测试,几乎不可能。

  • 推荐:依赖注入 (Dependency Injection),使用 IService 接口。

3. 静态构造函数的坑
静态类也有构造函数 static MyClass() { ... }
它在类被第一次调用时触发。如果这里面报错了,整个程序关于这个类的部分就彻底废了,而且报错信息往往很隐晦(TypeInitializationException)。

总结:如何重塑你对静态类的认知?

  1. 工具人 (Utils/Helpers):Math, Path, Convert,这是它最本分的职责。(你已经会的)
  2. 黑板 (Global State):存放 CurrentUser, Config 等全局唯一、大家都需要看一眼的数据。(登录传值就是这个)
  3. 语法糖 (Extensions):通过 this 关键字,让 C# 代码写起来更流畅,这是 WPF 开发的高频技巧。
  4. 生命周期:它与程序同生共死。用好了是方便,用不好就是内存泄漏多线程冲突的温床。