CodeGym /课程 /C# SELF /Extension Members:属性

Extension Members:属性

C# SELF
第 18 级, 课程 3
可用

1. 入门

以前只能给类加 Extension-methods。但想加属性(property)作为扩展是不行的!比如你想让 DateTime 有个 IsWeekend 属性,只能用方法实现,这样其实不太方便——尤其你想要 date.IsWeekend 这种语法,而不是 date.IsWeekend()。

随着 C# 14 的发布,梦想成真:现在可以用类似方法的方式写扩展属性了。你可以给已有类型加新属性,不用改源码,用起来就像普通属性一样!

哪里真的有用?

  • 对于 .NET 标准类型和第三方库,源码你改不了。
  • 在 view-model、UI、计算属性等场景下,写“虚拟”属性很方便。
  • 想要 .SomeCalculatedProperty 这种优雅语法,不想加括号。

2. C# 14 的新语法

在 C# 14 里,扩展不再用 this 关键字了。引入了新的操作符 extension(Type this)。用它就能给类加虚拟成员了。

老语法

public static class StringExtensions
{
    public static bool IsNullOrWhiteSpace(this string str)
        => string.IsNullOrWhiteSpace(str);
}

新语法


public static class Enumerable
{
    extension(TSource source)
    {
        // 对象的虚拟成员
    }

    extension(TSource)
    {
        // 静态虚拟成员
    }
}
新的 extension members 语法(C# 14+)

注意! 这个新的 extension 语法只在 C# 14+ 和 .NET 10+ 下能用。如果你用的是 .NET 9,那就 用不了。但如果你想试试,而 .NET 10 还没正式发布,可以装 .NET 10 preview 5+
提示! 我们的 C# 课程用的都是 C# 14+ 的最新特性,甚至有些还没正式发布。2025 年秋天 .NET 10 就出来了,等你学完课程,绝对是 C# 最前沿的玩家!😎

3. 扩展属性的语法

长啥样?

假如你想给 DateTime 类型加个属性,判断是不是周末:

public static class DateTimeExtensions
{
    public static bool IsWeekend(this DateTime date) => 
        date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
}

但这还是老的 Extention Method,用法是:myDate.IsWeekend()

新风格——Extension Property


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public bool IsWeekend // 扩展属性!
        {
            get => source.DayOfWeek == DayOfWeek.Saturday || source.DayOfWeek == DayOfWeek.Sunday;
        }        
    }
}

现在你可以这样写:

DateTime today = DateTime.Now;
if (today.IsWeekend)
{
    Console.WriteLine("可以多睡一会儿!");
}

4. extension properties 有哪些类型

不只是只读属性,还能加读写属性:


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public int YearFrom1900 // 扩展属性!
        {
            get => source.Year - 1900;
            set => source.Year = value + 1900; 
        }        
    }
}
可读写的扩展属性
注意: 对于可读写的 extension properties,你要明白它们会改原对象,但不能像自动字段那样单独存新值。如果你想加一个对象本身没有的属性,需要额外的存储(比如字典),这就超出基础用法了。

5. 静态属性

新语法让你不仅能给对象加虚拟属性,还能给类加!没错,现在可以给类加虚拟静态属性。语法几乎一样:


public static class DateTimeExtensions
{
    // 新语法:DateTime 的 extension block(C# 14)
    extension(DateTime)
    {
        // 用于存储当前用户时区的静态属性
        private static TimeZoneInfo _currentTimeZone = TimeZoneInfo.Local;

        // 扩展属性,访问当前时区
        public static TimeZoneInfo CurrentTimeZone
        {
            get => _currentTimeZone;
            set => _currentTimeZone = value ?? TimeZoneInfo.Local;
        }

        // 返回用户时区的时间
        public DateTime InCurrentTimeZone
        {
            get => TimeZoneInfo.ConvertTime(this, CurrentTimeZone);
        }
    }
}

6. 示例:扩展 Dog 类!

让我们继续完善我们的狗狗应用,这个例子我们每节课都在用。

假设现在我们有狗狗,还有它们的疫苗接种列表(List<DateTime>),但 Dog 类是第三方库的,不能改源码。我们想加个属性:今年所有必需疫苗都打了吗?

老方法(扩展方法):

public static class DogExtensions
{
    public static bool AllVaccinatedThisYear(this Dog dog)
    {
        return dog.Vaccinations.Any(v => v.Year == DateTime.Now.Year);
    }
}
// 用法:
if (myDog.AllVaccinatedThisYear())
    Console.WriteLine("狗狗很健康!");

新方法(扩展属性):


public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// 用法:
if (myDog.AllVaccinatedThisYear)
    Console.WriteLine("狗狗很健康!");

完整例子

// Dog 类——比如第三方库里的:
public class Dog
{
    public string Name { get; set; }
    public List<DateTime> Vaccinations { get; set; } = new List<DateTime>();
}

// 扩展类,带 extension block:
public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// 在程序里:
var myDog = new Dog { Name = "Buddy" };
myDog.Vaccinations.Add(new DateTime(DateTime.Now.Year, 2, 16)); // 今年的疫苗

Console.WriteLine($"{myDog.Name}: 打疫苗了吗? {(myDog.AllVaccinatedThisYear ? "是" : "否")}");

7. 表格:扩展方法 vs 扩展属性

扩展方法 扩展属性
调用语法 obj.Method() obj.Property
能传参数 能 不能(只有 this obj)
能写入 不适用 实现了才行
适合 View/UI 有时 适合(双向绑定、lambda)
反射可见性 不可见 不可见

8. 潜在陷阱和常见错误

什么时候 extension 属性帮不上忙

  • 它们不能完全替代真实字段:没法访问对象的私有成员。
  • 不能加 自动 状态变更(比如不能直接跟踪原类值的变化)。
  • 不能用 extension property 实现接口或抽象类。

命名冲突

如果原类后来加了和你扩展属性同名的属性,优先用“原生”属性,不会用你的扩展。所以起名要小心,特别是用 .NET 的公共类型时。

序列化/反射问题

Extension property 其实不是“真实”成员,只是语法糖。所以用反射或序列化工具时,扩展属性可能不可见。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION