C# 12 中的 Primary Constructor

本文介绍了C#12中新引入的PrimaryConstructor特性,展示了如何在类和struct中使用它作为字段,以及简化构造函数依赖注入。同时提及了record类型与非record类型在PrimaryConstructor上的差异。
摘要由CSDN通过智能技术生成

C# 12 中的 Primary Constructor

Intro

在即将到来的 C# 12 中,觉得 Primary Constructor 算是比较实用的特性之一了,和大家分享一下

Sample

直接来看几个使用示例吧

file class Animal(string name) 
{
    public string Name => name;
}

file sealed class Cat(string Name) : Animal(Name){}

file struct Point(int x, int y)
{
    public int X => x;
    public int Y => y;
}

可以用于class 也可以用于 struct,相当于一个字段,可以直接在类型中直接引用

类似的,对于构造函数依赖注入,我们也可以使用这个特性来进行简化,就不需要再手动声明字段了,示例如下:

file sealed class CrudHelper<T>(IIdGenerator idGenerator)
{
    public CrudHelper(): this(GuidIdGenerator.Instance)
    {
    }
    public string Create(T t)
    {
        // Biu...
        return idGenerator.NewId();
    }
}

What's inside

那么它实际是如何工作的呢,我们反编译看下前面的 CrudHelper, 反编译结果如下:

internal sealed class <PrimaryConstructorSample>FB47A7FDD6A2F32139FF8A728FBC1FE107CACFA93EA6284446EA366B2274F584F__CrudHelper<T>
{
 [CompilerGenerated]
 [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 private IIdGenerator <idGenerator>P;

 public <PrimaryConstructorSample>FB47A7FDD6A2F32139FF8A728FBC1FE107CACFA93EA6284446EA366B2274F584F__CrudHelper(IIdGenerator idGenerator)
 {
  <idGenerator>P = idGenerator;
  base..ctor();
 }

 public <PrimaryConstructorSample>FB47A7FDD6A2F32139FF8A728FBC1FE107CACFA93EA6284446EA366B2274F584F__CrudHelper()
  : this((IIdGenerator)GuidIdGenerator.Instance)
 {
 }

 public string Create(T t)
 {
  return <idGenerator>P.NewId();
 }
}

从反编译结果可以看出来,实际就是生成了一个私有的字段,并且在构造方法里进行了赋值,而且字段上增加了 DebuggerBrowsable(DebuggerBrowsableState.Never) Attribute

仔细看的话会发现字段不是 readonly 的,关于这一点,第一眼觉得,是不是可以声明称 readonly 的字段,目前来说字段是可以被修改的,不是 readonly 的,而且大概率以后也会保持这样,这样有着更大的灵活性,以后如果允许指定 access level(public/protected/..) 也不需要再变更这个字段,详细可以参考这个讨论:https://github.com/dotnet/csharplang/discussions/7377

所以下面的示例也是可以编译通过的

file sealed class Dog(string name, int age) : Animal(name)
{
    public int Age => age;

    public void OneYearPassed()
    {
        age++;
        System.Console.WriteLine("One year passed, now age is: {0}", age);
    }
}


var dog = new Dog("Spike", 3);
Console.WriteLine(dog.Age);
dog.OneYearPassed();

输出结果如下:

3
One year passed, now age is: 4

More

需要注意,非 record 类型的 primary constructor 和 record 类型的 primary constructor 并不相同,差别较大,对于 record 类型来说会生成属性而不是字段,这一点区别很大,而且目前来说字段是私有的,不能被外部类型直接引用,其次 record 类型会生成 deconstructor 和自定义的相等性比较等

简而言之,非 record 类型的 primary constructor 更简单一些,只有一个字段

更多 record 类型介绍可以参考之前的介绍文章:

record class: C# 9 新特性 — record 解读

record struct: C# 10 新特性 —— struct 更新

References

  • https://github.com/dotnet/csharplang/issues/2691

  • https://github.com/dotnet/csharplang/blob/main/proposals/primary-constructors.md

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp12Sample/PrimaryConstructorSample.cs

  • https://github.com/WeihanLi/OpenReservation.ScheduleServices/blob/08616e713d059059a7134f328469f187feaa3d10/OpenReservation.ScheduleServices/JobRegisterService.cs

  • https://learn.microsoft.com/dotnet/csharp/whats-new/csharp-12#primary-constructors

  • record class: C# 9 新特性 — record 解读

  • record struct: C# 10 新特性 —— struct 更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值