静态类与类型扩展

24 阅读6分钟

专栏导航

前一章我们学习了类和对象的基础,这一章我们将学习两种特殊的类使用方式:静态类和类型扩展。它们可以让我们的代码更加灵活和强大。

静态类(Static Class)

什么是静态类?

静态类 是一种特殊的类,它不能被实例化(不能创建对象),直接通过类名访问其成员。

类比:

  • 普通类 = 手机店(可以创建多部手机)
  • 静态类 = 广播塔(只有一个,直接使用,不需要创建)

静态类的特点

不能创建对象:不需要使用 new 关键字 ✅ 所有成员必须是静态的:方法和属性都需要加 static 关键字 ✅ 只能有一个实例:全局共享 ✅ 用于工具方法:如 Math 类、Console 类

静态类 vs 普通类

特性普通类静态类
创建对象需要 new不能创建
访问方式对象.方法类名.方法
实例可以有多个只有一个
成员可包含静态和非静态必须都是静态
适用场景需要多个实例工具类、辅助方法

为什么要用静态类?

场景一:工具方法

// 不使用静态类 - 每次都要创建对象
MathHelper helper = new MathHelper();
double result = helper.Add(5, 3);

// 使用静态类 - 直接调用
double result = MathHelper.Add(5, 3);

场景二:全局共享数据

// 配置信息
ConfigHelper.GetDatabaseConnection();
ConfigHelper.GetApiKey();

定义静态类

// 使用 static 关键字定义静态类
static class 类名
{
    // 静态属性
    public static 数据类型 属性名 { get; set; }

    // 静态方法
    public static 返回值类型 方法名(参数列表)
    {
        // 方法体
    }
}

示例:MathHelper 静态类

// 定义静态工具类
static class MathHelper
{
    // 静态方法:计算两个数的和
    public static double Add(double a, double b)
    {
        return a + b;
    }

    // 静态方法:计算两个数的差
    public static double Subtract(double a, double b)
    {
        return a - b;
    }

    // 静态方法:计算两个数的积
    public static double Multiply(double a, double b)
    {
        return a * b;
    }

    // 静态方法:计算圆面积
    public static double CalculateCircleArea(double radius)
    {
        return 3.1415926 * radius * radius;
    }

    // 静态方法:判断是否为偶数
    public static bool IsEven(int number)
    {
        return number % 2 == 0;
    }
}

// 使用静态类
class Program
{
    static void Main(string[] args)
    {
        // 直接通过类名调用,不需要创建对象
        double sum = MathHelper.Add(5, 3);
        Console.WriteLine($"5 + 3 = {sum}");

        double difference = MathHelper.Subtract(10, 4);
        Console.WriteLine($"10 - 4 = {difference}");

        double area = MathHelper.CalculateCircleArea(5);
        Console.WriteLine($"半径为5的圆面积:{area:F2}");

        bool isEven = MathHelper.IsEven(7);
        Console.WriteLine($"7 是偶数吗?{isEven}");
    }
}

输出:

5 + 3 = 8
10 - 4 = 6
半径为5的圆面积:78.54
7 是偶数吗?False

静态属性

静态属性在所有对象之间共享。

static class Counter
{
    private static int count = 0;  // 静态字段

    // 静态属性
    public static int Count
    {
        get { return count; }
    }

    // 静态方法
    public static void Increment()
    {
        count++;
    }

    public static void Reset()
    {
        count = 0;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"初始计数:{Counter.Count}");

        Counter.Increment();
        Counter.Increment();
        Counter.Increment();

        Console.WriteLine($"增加3次后:{Counter.Count}");

        Counter.Reset();
        Console.WriteLine($"重置后:{Counter.Count}");
    }
}

输出:

初始计数:0
增加3次后:3
重置后:0

常见的静态类

1. Math 类(数学运算)

double max = Math.Max(5, 10);      // 最大值:10
double min = Math.Min(5, 10);      // 最小值:5
double abs = Math.Abs(-5);         // 绝对值:5
double power = Math.Pow(2, 3);     // 幂运算:8
double sqrt = Math.Sqrt(16);       // 平方根:4
double pi = Math.PI;                // 圆周率:3.14159...

2. Console 类(输入输出)

Console.WriteLine("输出信息");     // 输出
string input = Console.ReadLine();  // 输入
Console.Clear();                    // 清屏

3. DateTime 类(时间操作)

DateTime now = DateTime.Now;        // 当前时间
string today = DateTime.Today.ToString();
int year = DateTime.Now.Year;       // 当前年份

实践:StringHelper 静态工具类

using System;

namespace Week9Practice
{
    // 定义静态字符串工具类
    static class StringHelper
    {
        // 反转字符串
        public static string Reverse(string text)
        {
            char[] chars = text.ToCharArray();
            Array.Reverse(chars);
            return new string(chars);
        }

        // 判断是否为回文
        public static bool IsPalindrome(string text)
        {
            string reversed = Reverse(text);
            return text.ToLower() == reversed.ToLower();
        }

        // 统计单词数量
        public static int CountWords(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return 0;

            string[] words = text.Split(new[] { ' ', '\t', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            return words.Length;
        }

        // 生成重复字符串
        public static string Repeat(string text, int count)
        {
            string result = "";
            for (int i = 0; i < count; i++)
            {
                result += text;
            }
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 字符串工具类演示 =====\n");

            // 反转字符串
            string original = "Hello World";
            string reversed = StringHelper.Reverse(original);
            Console.WriteLine($"原字符串:{original}");
            Console.WriteLine($"反转后:{reversed}");

            Console.WriteLine();

            // 判断回文
            string[] testWords = { "racecar", "level", "hello", "radar" };
            foreach (string word in testWords)
            {
                bool isPalindrome = StringHelper.IsPalindrome(word);
                Console.WriteLine($"'{word}' 是回文吗?{isPalindrome}");
            }

            Console.WriteLine();

            // 统计单词
            string sentence = "C# is an amazing programming language";
            int wordCount = StringHelper.CountWords(sentence);
            Console.WriteLine($"句子:'{sentence}'");
            Console.WriteLine($"单词数量:{wordCount}");

            Console.WriteLine();

            // 重复字符串
            string pattern = "abc";
            int repeatCount = 5;
            string repeated = StringHelper.Repeat(pattern, repeatCount);
            Console.WriteLine($"'{pattern}' 重复 {repeatCount} 次:{repeated}");
        }
    }
}

输出:

===== 字符串工具类演示 =====

原字符串:Hello World
反转后:dlroW olleH

'racecar' 是回文吗?True
'level' 是回文吗?True
'hello' 是回文吗?False
'radar' 是回文吗?True

句子:'C# is an amazing programming language'
单词数量:6

'abc' 重复 5 次:abcabcabcabcabc

实践:Validator 静态验证类

using System;

namespace Week9Practice
{
    // 定义静态验证工具类
    static class Validator
    {
        // 验证邮箱格式
        public static bool IsValidEmail(string email)
        {
            if (string.IsNullOrWhiteSpace(email))
                return false;

            // 简单验证:包含 @ 和 .
            return email.Contains("@") && email.Contains(".");
        }

        // 验证手机号(11位数字)
        public static bool IsValidPhoneNumber(string phone)
        {
            if (string.IsNullOrWhiteSpace(phone) || phone.Length != 11)
                return false;

            // 检查是否全是数字
            foreach (char c in phone)
            {
                if (!char.IsDigit(c))
                    return false;
            }
            return true;
        }

        // 验证年龄
        public static bool IsValidAge(int age)
        {
            return age >= 0 && age <= 150;
        }

        // 验证密码强度(至少6位)
        public static bool IsValidPassword(string password)
        {
            return !string.IsNullOrWhiteSpace(password) && password.Length >= 6;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 数据验证工具 =====\n");

            // 验证邮箱
            string[] emails = { "test@example.com", "invalid-email", "user@", "good@domain.org" };
            foreach (string email in emails)
            {
                bool isValid = Validator.IsValidEmail(email);
                Console.WriteLine($"'{email}' - {(isValid ? "✓ 有效" : "✗ 无效")}");
            }

            Console.WriteLine();

            // 验证手机号
            string[] phones = { "13800138000", "123", "12345678901", "1234567890a", "15912345678" };
            foreach (string phone in phones)
            {
                bool isValid = Validator.IsValidPhoneNumber(phone);
                Console.WriteLine($"'{phone}' - {(isValid ? "✓ 有效" : "✗ 无效")}");
            }

            Console.WriteLine();

            // 验证年龄
            int[] ages = { 25, -5, 150, 200, 0 };
            foreach (int age in ages)
            {
                bool isValid = Validator.IsValidAge(age);
                Console.WriteLine($"年龄 {age} - {(isValid ? "✓ 有效" : "✗ 无效")}");
            }

            Console.WriteLine();

            // 验证密码
            string[] passwords = { "123456", "123", "abc", "password123", "" };
            foreach (string password in passwords)
            {
                bool isValid = Validator.IsValidPassword(password);
                string masked = password.Length > 0 ? new string('*', password.Length) : "(空)";
                Console.WriteLine($"密码 '{masked}' - {(isValid ? "✓ 有效" : "✗ 无效")}");
            }
        }
    }
}

输出:

===== 数据验证工具 =====

'test@example.com' - ✓ 有效
'invalid-email' - ✗ 无效
'user@' - ✗ 无效
'good@domain.org' - ✓ 有效

'13800138000' - ✓ 有效
'123' - ✗ 无效
'12345678901' - ✗ 无效
'1234567890a' - ✗ 无效
'15912345678' - ✓ 有效

年龄 25 - ✓ 有效
年龄 -5 - ✗ 无效
年龄 150 - ✓ 有效
年龄 200 - ✗ 无效
年龄 0 - ✓ 有效

密码 '******' - ✓ 有效
密码 '***' - ✗ 无效
密码 '***' - ✗ 无效
密码 '************' - ✓ 有效
密码 '(空)' - ✗ 无效

静态类的限制

不能创建实例

// 错误!静态类不能创建对象
MathHelper helper = new MathHelper();

不能继承其他类或被继承

// 错误!静态类不能继承
static class MyHelper : BaseClass { }

// 错误!静态类不能被继承
static class BaseClass { }
class MyClass : BaseClass { }

不能包含非静态成员

// 错误!静态类中的成员必须是静态的
static class MyClass
{
    public void NormalMethod() { }  // 错误
    public int MyProperty { get; set; }  // 错误
}

普通类中的静态成员

普通类也可以包含静态成员,静态成员属于类本身,不属于某个对象。

class Student
{
    public string Name { get; set; }      // 实例属性
    private static int totalCount = 0;     // 静态字段

    public Student(string name)
    {
        Name = name;
        totalCount++;  // 每创建一个对象,计数器加1
    }

    // 静态方法
    public static int GetTotalCount()
    {
        return totalCount;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"初始学生数:{Student.GetTotalCount()}");

        Student s1 = new Student("张三");
        Student s2 = new Student("李四");
        Student s3 = new Student("王五");

        Console.WriteLine($"当前学生数:{Student.GetTotalCount()}");  // 3

        // 注意:不能通过对象访问静态成员
        // s1.GetTotalCount();  // 错误!
    }
}

输出:

初始学生数:0
当前学生数:3

什么时候使用静态类?

适合使用的场景:

  • 工具方法(如 MathHelper、StringHelper)
  • 验证逻辑(如 Validator)
  • 配置管理(如 ConfigHelper)
  • 常量集合(如 Constants)
  • 无状态的操作(输入输出确定)

不适合使用的场景:

  • 需要维护状态
  • 需要继承
  • 需要多态
  • 需要多个独立实例

小贴士

💡 命名建议: 静态工具类通常以 HelperUtilValidator 等结尾

MathHelper, StringHelper, ConfigHelper
StringUtil, DateUtil
Validator, EmailValidator

💡 不要滥用静态类:

  • 如果需要维护对象的状态,使用普通类
  • 如果需要依赖注入,使用普通类
  • 如果需要测试,普通类更容易 mock

类型扩展(Extension Methods)

什么是扩展方法?

扩展方法 是一种特殊的静态方法,可以为现有类型(如 string、int、甚至第三方库的类)添加新方法,而不需要修改原始类的代码继承该类

类比:

  • 原始类 = 一把普通剪刀
  • 扩展方法 = 给剪刀加装了一个开瓶器功能
  • 不需要重新造剪刀,只是在它的基础上"扩展"了能力

为什么需要扩展方法?

场景一:为系统类型添加方法

你想给 string 类型添加一个"是否为手机号"的判断方法,但不能修改 .NET 源代码:

// 不使用扩展方法 - 调用不方便
bool isValid = Validator.IsPhoneNumber("13800138000");

// 使用扩展方法 - 调用更自然
bool isValid = "13800138000".IsPhoneNumber();

场景二:第三方库类型

使用第三方库时,想给它的类添加功能,但没有源代码:

// 为第三方类添加方法
someObject.CalculateAge();  // 调用像自己的方法一样自然

扩展方法的语法

// 1. 必须在静态类中定义
static class 扩展类名
{
    // 2. 方法必须是静态的
    public static 返回值类型 方法名(this 要扩展的类型 参数名)
    {
        // 方法体
    }
}

关键点:

  • 第一个参数前必须加 this 关键字
  • this 关键字指定要扩展的类型
  • 调用时,第一个参数不需要传值(会被自动填充)

示例:为 string 类型添加扩展方法

using System;

namespace Week9Practice
{
    // 定义静态类,包含扩展方法
    static class StringExtensions
    {
        // 扩展方法1:判断是否为手机号
        public static bool IsPhoneNumber(this string text)
        {
            if (string.IsNullOrWhiteSpace(text) || text.Length != 11)
                return false;

            foreach (char c in text)
            {
                if (!char.IsDigit(c))
                    return false;
            }
            return true;
        }

        // 扩展方法2:判断是否为邮箱
        public static bool IsEmail(this string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return false;

            return text.Contains("@") && text.Contains(".");
        }

        // 扩展方法3:首字母大写
        public static string ToTitleCase(this string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return text;

            if (text.Length == 1)
                return text.ToUpper();

            return char.ToUpper(text[0]) + text.Substring(1);
        }

        // 扩展方法4:反转字符串
        public static string Reverse(this string text)
        {
            char[] chars = text.ToCharArray();
            Array.Reverse(chars);
            return new string(chars);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== String 类型扩展演示 =====\n");

            // 使用扩展方法 - 看起来像 string 的原生方法!
            string phone1 = "13800138000";
            string phone2 = "123";

            Console.WriteLine($"'{phone1}' 是手机号吗?{phone1.IsPhoneNumber()}");
            Console.WriteLine($"'{phone2}' 是手机号吗?{phone2.IsPhoneNumber()}");

            Console.WriteLine();

            // 邮箱验证
            string email1 = "test@example.com";
            string email2 = "invalid-email";

            Console.WriteLine($"'{email1}' 是邮箱吗?{email1.IsEmail()}");
            Console.WriteLine($"'{email2}' 是邮箱吗?{email2.IsEmail()}");

            Console.WriteLine();

            // 首字母大写
            string name1 = "hello";
            string name2 = "world";

            Console.WriteLine($"'{name1}' -> {name1.ToTitleCase()}");
            Console.WriteLine($"'{name2}' -> {name2.ToTitleCase()}");

            Console.WriteLine();

            // 反转字符串
            string original = "C# Programming";
            Console.WriteLine($"'{original}' 反转后:'{original.Reverse()}'");
        }
    }
}

输出:

===== String 类型扩展演示 =====

'13800138000' 是手机号吗?True
'123' 是手机号吗?False

'test@example.com' 是邮箱吗?True
'invalid-email' 是邮箱吗?False

'hello' -> Hello
'world' -> World

'C# Programming' 反转后:'gnimmargorP #C'

示例:为 int 类型添加扩展方法

static class IntExtensions
{
    // 判断是否为偶数
    public static bool IsEven(this int number)
    {
        return number % 2 == 0;
    }

    // 判断是否为奇数
    public static bool IsOdd(this int number)
    {
        return number % 2 != 0;
    }

    // 判断是否为质数
    public static bool IsPrime(this int number)
    {
        if (number < 2) return false;

        for (int i = 2; i <= Math.Sqrt(number); i++)
        {
            if (number % i == 0)
                return false;
        }
        return true;
    }

    // 计算阶乘
    public static long Factorial(this int number)
    {
        if (number < 0) throw new ArgumentException("不能为负数");

        long result = 1;
        for (int i = 2; i <= number; i++)
        {
            result *= i;
        }
        return result;
    }

    // 格式化为中文
    public static string ToChinese(this int number)
    {
        string[] digits = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
        string result = "";

        foreach (char c in number.ToString())
        {
            if (char.IsDigit(c))
            {
                result += digits[int.Parse(c.ToString())];
            }
        }
        return result;
    }
}

class Program
{
    static void Main(string[] args)
    {
        int num = 7;

        Console.WriteLine($"{num} 是偶数吗?{num.IsEven()}");
        Console.WriteLine($"{num} 是奇数吗?{num.IsOdd()}");
        Console.WriteLine($"{num} 是质数吗?{num.IsPrime()}");

        Console.WriteLine();

        Console.WriteLine($"5! = {5.Factorial()}");
        Console.WriteLine($"10! = {10.Factorial()}");

        Console.WriteLine();

        int year = 2025;
        Console.WriteLine($"{year} 中文:{year.ToChinese()}");
    }
}

输出:

7 是偶数吗?False
7 是奇数吗?True
7 是质数吗?True

5! = 120
10! = 3628800

2025 中文:二零二五

示例:为自定义类添加扩展方法

// 自定义类
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void PrintInfo()
    {
        Console.WriteLine($"{Name}, {Age}岁");
    }
}

// 为 Person 类添加扩展方法
static class PersonExtensions
{
    // 判断是否成年
    public static bool IsAdult(this Person person)
    {
        return person.Age >= 18;
    }

    // 获取星座
    public static string GetZodiacSign(this Person person)
    {
        // 简化版:只作为示例
        int month = DateTime.Now.Month;
        int day = DateTime.Now.Day;

        // 实际应根据出生日期计算
        return "狮子座";  // 示例
    }

    // 格式化为问候语
    public static string ToGreeting(this Person person)
    {
        if (person.IsAdult())
            return $"你好,{person.Name} 先生/女士";
        else
            return $"你好,{person.Name} 小朋友";
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person adult = new Person("张三", 25);
        Person child = new Person("小明", 12);

        Console.WriteLine("=== 成年人 ===");
        adult.PrintInfo();
        Console.WriteLine($"是否成年:{adult.IsAdult()}");
        Console.WriteLine($"问候:{adult.ToGreeting()}");

        Console.WriteLine();

        Console.WriteLine("=== 小朋友 ===");
        child.PrintInfo();
        Console.WriteLine($"是否成年:{child.IsAdult()}");
        Console.WriteLine($"问候:{child.ToGreeting()}");
    }
}

输出:

=== 成年人 ===
张三, 25岁
是否成年:True
问候:你好,张三 先生/女士

=== 小朋友 ===
小明, 12岁
是否成年:False
问候:你好,小明 小朋友

扩展方法的注意事项

⚠️ 注意事项:

  1. 必须在静态类中定义
// 正确
static class MyExtensions
{
    public static void MyMethod(this string text) { }
}

// 错误
class MyExtensions
{
    public static void MyMethod(this string text) { }
  1. 方法本身必须是静态的
// 错误
static class MyExtensions
{
    public void MyMethod(this string text) { }  // 缺少 static
}
  1. 第一个参数必须是扩展类型
// 正确:为 string 扩展
public static void MyMethod(this string text) { }

// 正确:为 int 扩展
public static void MyMethod(this int number) { }

// 正确:为泛型类型扩展
public static void MyMethod(this T item) { }
  1. 扩展方法不能修改原始类的私有成员
class MyClass
{
    private int privateField = 10;
}

static class MyClassExtensions
{
    public static void TryAccess(this MyClass obj)
    {
        // 错误!无法访问私有字段
        // int x = obj.privateField;
    }
}
  1. 如果原始类有同名方法,会优先调用原始方法
class MyClass
{
    public void MyMethod()
    {
        Console.WriteLine("原始方法");
    }
}

static class MyClassExtensions
{
    public static void MyMethod(this MyClass obj)
    {
        Console.WriteLine("扩展方法");
    }
}

// 调用时,会使用原始方法
MyClass obj = new MyClass();
obj.MyMethod();  // 输出:原始方法

什么时候使用扩展方法?

适合使用的场景:

  • 为系统类型(string、int、DateTime 等)添加常用方法
  • 为第三方库的类型添加功能,但没有源代码
  • 提高代码可读性和调用便利性
  • 将辅助方法组织到更合理的位置

常用场景示例:

  • 数据验证
  • 格式化转换
  • 业务逻辑扩展
  • 集合操作

不适合使用的场景:

  • 可以直接修改原始类代码时
  • 需要访问类的私有成员时
  • 会与原始类方法产生冲突时
  • 需要维护复杂状态时

常用扩展方法建议

数据验证类扩展:

string.IsEmail()
string.IsPhoneNumber()
string.IsUrl()
int.IsEven()
int.IsOdd()
int.IsPrime()

格式化类扩展:

string.ToTitleCase()
string.Reverse()
string.Mask()
int.ToChinese()
int.ToOrdinal()  // 1st, 2nd, 3rd...

日期时间扩展:

DateTime.IsWeekend()
DateTime.IsToday()
DateTime.Age()

小贴士

💡 命名规范: 扩展方法类名通常以 Extensions 结尾

StringExtensions, IntExtensions, ListExtensions

💡 组织方式: 将相关的扩展方法放在同一个静态类中

static class StringExtensions
{
    // 所有 string 相关的扩展方法
}

static class DateTimeExtensions
{
    // 所有 DateTime 相关的扩展方法
}

💡 LINQ 本质上就是扩展方法!

// 这些都是扩展方法
numbers.Where(n => n > 5)
numbers.Select(n => n * 2)
numbers.OrderBy(n => n)

本章总结

  • ✅ 理解了静态类的概念和特点
  • ✅ 掌握了静态类的定义和使用
  • ✅ 理解了静态类与普通类的区别
  • ✅ 学会了创建静态工具类
  • ✅ 掌握了扩展方法的概念和语法
  • ✅ 学会了为现有类型添加扩展方法
  • ✅ 理解了扩展方法的注意事项和使用场景