专栏导航
- ← 上一篇:对象的行为 - 对象中的方法
- ← 第一篇:编程世界初探
- 专栏目录
前一章我们学习了类和对象的基础,这一章我们将学习两种特殊的类使用方式:静态类和类型扩展。它们可以让我们的代码更加灵活和强大。
静态类(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)
- 无状态的操作(输入输出确定)
❌ 不适合使用的场景:
- 需要维护状态
- 需要继承
- 需要多态
- 需要多个独立实例
小贴士
💡 命名建议: 静态工具类通常以 Helper、Util、Validator 等结尾
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
问候:你好,小明 小朋友
扩展方法的注意事项
⚠️ 注意事项:
- 必须在静态类中定义
// 正确
static class MyExtensions
{
public static void MyMethod(this string text) { }
}
// 错误
class MyExtensions
{
public static void MyMethod(this string text) { }
- 方法本身必须是静态的
// 错误
static class MyExtensions
{
public void MyMethod(this string text) { } // 缺少 static
}
- 第一个参数必须是扩展类型
// 正确:为 string 扩展
public static void MyMethod(this string text) { }
// 正确:为 int 扩展
public static void MyMethod(this int number) { }
// 正确:为泛型类型扩展
public static void MyMethod(this T item) { }
- 扩展方法不能修改原始类的私有成员
class MyClass
{
private int privateField = 10;
}
static class MyClassExtensions
{
public static void TryAccess(this MyClass obj)
{
// 错误!无法访问私有字段
// int x = obj.privateField;
}
}
- 如果原始类有同名方法,会优先调用原始方法
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)
本章总结
- ✅ 理解了静态类的概念和特点
- ✅ 掌握了静态类的定义和使用
- ✅ 理解了静态类与普通类的区别
- ✅ 学会了创建静态工具类
- ✅ 掌握了扩展方法的概念和语法
- ✅ 学会了为现有类型添加扩展方法
- ✅ 理解了扩展方法的注意事项和使用场景