You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#抽象Record转主构造函数后,派生类可改Type属性是Rider Bug吗?

问题:Rider的主构造函数转换建议是否存在Bug?

原始代码

public enum TransactionType{Foo,Bar}

public abstract record Transaction
{
    public TransactionType Type { get; }

    protected Transaction(TransactionType type)
    {
        Type = type;
    }
}

public record InheritedTransactionBarOnly: Transaction
{
    public InheritedTransactionBarOnly(): base(TransactionType.Bar){}
}

public static class Program
{
    public static void Main()
    {
        var iWantThis = new InheritedTransactionBarOnly();
        //var iDontWantThis = new InheritedTransactionBarOnly{Type = TransactionType.Foo}; // this shouldn't be possible
    }
}

Rider的转换建议

JetBrains Rider建议将上述代码中的基类Transaction转换为使用主构造函数的形式,转换后的代码如下:

public abstract record Transaction(TransactionType Type)
{
}

转换后的问题

转换后出现了不符合原始设计意图的问题:派生记录类型的实例可以在初始化时修改Type属性,而原始代码中这是不允许的。示例代码如下:

var inheritedTransaction = new InheritedTransactionBarOnly
{
   Type = TransactionType.Foo // 转换后可编译,转换前不允许
};

解答

这不是你对C#主构造函数的理解错误,而是Rider的转换建议存在缺陷。

原因如下:

  • 原始代码中,Type是只读自动属性(仅包含get访问器),仅能在基类构造函数中赋值,派生类通过无参构造函数固定传入TransactionType.Bar,外部完全无法修改该属性。
  • 使用主构造函数定义抽象记录时,C#编译器默认会为构造函数参数生成带有init访问器的自动属性。init访问器允许在对象初始化器中修改属性值,这直接破坏了原始代码的设计逻辑——确保InheritedTransactionBarOnly的Type始终为Bar。

正确的转换方式应该是手动显式定义只读属性,避免编译器生成可初始化的属性:

public abstract record Transaction(TransactionType type)
{
    public TransactionType Type { get; } = type;
}

这种方式保留了原始代码的只读特性,外部无法通过任何方式修改Type属性,符合最初的设计意图。

内容的提问来源于stack exchange,提问作者Ilya Chernomordik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:40:24