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