0Pricing
C# Academy · 课时

静态设计与实例设计

判断何时适合使用静态状态。

静态设计与实例设计 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

一项设计决策

选择静态成员还是实例成员是一项设计决策。关键问题是:行为取决于每个对象的状态,还是只取决于输入?

using System;

class Program
{
    // Depends only on inputs -> good static method
    static int Max(int a, int b) => a > b ? a : b;

    static void Main()
    {
        Console.WriteLine(Max(3, 8));
    }
}

无状态逻辑使用静态成员

如果方法不需要任何实例字段,只是转换输入,请将其设为 static。数学辅助方法和格式化方法就是典型例子。

using System;

static class Convert2
{
    public static double MilesToKm(double miles) => miles * 1.60934;
}

class Program
{
    static void Main()
    {
        Console.WriteLine(Convert2.MilesToKm(10).ToString("0.0") + " km");
    }
}

有状态数据使用实例

如果行为取决于每个对象中各不相同的数据,请使用实例。每个对象都携带自己的状态。

using System;

class Counter
{
    private int _count;
    public void Increment() => _count++;
    public int Value => _count;
}

class Program
{
    static void Main()
    {
        var a = new Counter();
        var b = new Counter();
        a.Increment();
        a.Increment();
        b.Increment();
        Console.WriteLine("a=" + a.Value + ", b=" + b.Value);
    }
}

共享状态就是全局状态

静态字段本质上是全局状态。当程序的许多部分都读取和写入它时,可能会引发隐蔽的错误。如果每个用户都需要自己的副本,请优先使用实例状态。

using System;

class BadCart
{
    public static int Items;   // shared by EVERYONE -> usually wrong for a cart
}

class Program
{
    static void Main()
    {
        BadCart.Items++;
        BadCart.Items++;
        Console.WriteLine("All carts share: " + BadCart.Items);
    }
}

可测试性

纯静态方法很容易测试:相同的输入得到相同的输出。然而,静态可变状态会在测试之间泄漏,使测试变得脆弱。

using System;

static class Calc
{
    public static int Add(int a, int b) => a + b;   // trivially testable
}

class Program
{
    static void Main()
    {
        Console.WriteLine(Calc.Add(2, 2) == 4 ? "pass" : "fail");
    }
}

工厂辅助方法使用静态成员

静态工厂方法是创建已配置实例并为其提供描述性名称的简洁方式。

using System;

class Color
{
    public int R, G, B;
    private Color(int r, int g, int b) { R = r; G = g; B = b; }

    public static Color Red() => new Color(255, 0, 0);
    public static Color White() => new Color(255, 255, 255);
}

class Program
{
    static void Main()
    {
        var c = Color.Red();
        Console.WriteLine(c.R + "," + c.G + "," + c.B);
    }
}

不要只为省略 new 而使用静态成员

不要为了避免创建对象就把所有内容都设为静态。如果某个类型表示一个带有状态的事物,使用实例可以让状态彼此隔离,更加安全。

using System;

class Player
{
    public string Name;
    public int Health = 100;
    public void TakeDamage(int d) => Health -= d;
}

class Program
{
    static void Main()
    {
        var p1 = new Player { Name = "A" };
        var p2 = new Player { Name = "B" };
        p1.TakeDamage(30);
        Console.WriteLine(p1.Name + ": " + p1.Health + ", " + p2.Name + ": " + p2.Health);
    }
}

恰当地混合使用两者

许多类会使用静态成员保存共享常量或计数器,并使用实例成员保存每个对象的数据。这种组合完全符合惯用写法。

using System;

class Invoice
{
    private static int _seq = 1000;
    public readonly int Number;
    public decimal Amount;

    public Invoice(decimal amount)
    {
        Number = _seq++;
        Amount = amount;
    }
}

class Program
{
    static void Main()
    {
        var i1 = new Invoice(50m);
        var i2 = new Invoice(75m);
        Console.WriteLine(i1.Number + " / " + i2.Number);
    }
}

并发方面的注意事项

多个线程可能会同时访问共享的静态状态,从而带来竞态条件风险。默认情况下,限制在单个线程中的实例状态更加安全。

using System;

class SafeCalc
{
    // No shared mutable state -> naturally thread-safe
    public static long Factorial(int n)
    {
        long result = 1;
        for (int i = 2; i <= n; i++) result *= i;
        return result;
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine(SafeCalc.Factorial(5));
    }
}

一条简单的经验法则

静态 = 属于类型且不使用每个对象状态的行为。实例 = 读取或更改特定对象的行为。如果不确定,对于任何有状态的内容,请优先使用实例。

using System;

class Temperature
{
    public double Celsius;                                  // instance state
    public static double ToF(double c) => c * 9 / 5 + 32;   // stateless helper
}

class Program
{
    static void Main()
    {
        var t = new Temperature { Celsius = 30 };
        Console.WriteLine(Temperature.ToF(t.Celsius));
    }
}

综合运用

良好的设计会将无状态辅助逻辑(静态成员)与有状态模型(实例)分开,并将共享的可变状态有意识地控制在最低限度。

using System;

static class TaxRules
{
    public static decimal Apply(decimal amount, decimal rate) => amount + amount * rate;
}

class Sale
{
    public decimal Subtotal;
    public decimal Total(decimal rate) => TaxRules.Apply(Subtotal, rate);
}

class Program
{
    static void Main()
    {
        var s = new Sale { Subtotal = 100m };
        Console.WriteLine(s.Total(0.2m));
    }
}

快速检查

请测试您对静态与实例设计的理解。

回顾

对于只依赖输入的无状态逻辑(辅助方法、工厂方法、常量),请选择静态成员;对于与每个对象状态相关的行为,请选择实例成员。静态可变状态本质上是全局状态,更难测试,也可能引发并发错误,因此应将其保持在最低限度,并有意识地使用。

using System;

static class Util { public static int Sq(int n) => n * n; }

class Box { public int Size; public int Area() => Util.Sq(Size); }

class Program
{
    static void Main()
    {
        var box = new Box { Size = 4 };
        Console.WriteLine(box.Area());
    }
}

常见问题解答

「静态设计与实例设计」课时是免费的吗?

是的 — 「静态设计与实例设计」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「静态设计与实例设计」这节课中我会学到什么?

判断何时适合使用静态状态。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「静态设计与实例设计」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 静态方法与字段
  2. 用于实用功能的静态类
  3. 常量与 readonly 字段
  4. 静态设计与实例设计
← 返回 C# Academy