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

如何在C# Record中实现依赖其他属性的自动属性

问题描述

现有以下代码:

class AA(string a, string b, string? c) {
...
}

record BB {
    public string A { get; init; };
    public string B { get; init; };
    public string? C { get; init; };
    public string? D { get; init; };
}

我想给BB添加一个AA类型的属性,期望实现如下效果:

record BB {
    public string A { get; init; };
    public string B { get; init; };
    public string? C { get; init; };
    public string? D { get; init; };
    public AA Aa { get; } = new AA(A, B, C);
}

但这段代码无法编译,推测原因是初始化Aa时BB的成员还未完成初始化。

于是我尝试了下面的写法,仍然无法运行:

record BB {
    public string A { get; init; };
    public string B { get; init; };
    public string? C { get; init; };
    public string? D { get; init; };
    private Lazy<AA> _Aa = new Lazy(new AA(A, B, C));
    public AA Aa => _Aa.Value;
}

我原本以为这种写法可行,因为Lazy会在调用Lazy.Value时才执行new AA(A, B, C),而Aa和Lazy.Value会在BB对象初始化完成后才被调用。但即便new AA(...)执行时A、B、C已经初始化,编译器还是不允许这么写。

我希望在不编写复杂代码(如下方示例,仅作演示,不保证ReaderWriterLockSlim用法正确)的前提下实现需求:

record BB {
    public string A { get; init; };
    public string B { get; init; };
    public string? C { get; init; };
    public string? D { get; init; };
    private AA? _Aa;
    private readonly ReaderWriterLockSlim _AaLock = new();
    public AA Aa
    {
        get
        {
            try
            {
                _AaLock.EnterReadLock();
                if (_Aa == null)
                {
                    try
                    {
                        _AaLock.EnterUpgradeableReadLock();
                        if (_Aa == null) {
                            try
                            {
                                _AaLock.EnterWriteLock();
                                _Aa = new(A, B, C);
                            }
                            finally
                            {
                                _AaLock.ExitWriteLock();
                            }
                        }
                    }
                    finally
                    {
                        _AaLock.ExitUpgradeableReadLock();
                    }

                }
            }
            finally
            {
                _AaLock.ExitReadLock();
            }
        }
    }
}

我知道如果放弃init初始化方式,改用显式构造函数可以实现,但想了解是否有支持init对象创建方式的解决方案,即支持:

var bb = new BB() {
    A = "my A",
    B = "my B",
    C = "my C",
}

而非仅支持以下写法:

var bb = new BB("my A", "my B", "my C", null);
// BB的构造函数如下:
public BB(string a, string b, string? c, string? d) {
   A = a;
   B = b;
   C = c;
   D = d;
   Aa = new AA(a, b, c);
}
可行方案(已验证)

我已确认Ivan Petrov的方案可行,以下是测试代码:

AA类对应代码(SomeRecordKey)

namespace ZZZ_CodeTest.Records
{
    public record SomeRecordKey(string Field1, string Field2, string? Field3)
    {
        public string Field1 { get; init; } = Field1;
        public string Field2 { get; init; } = Field2;
        public string? Field3 { get; init; } = Field3;

    }
}

BB类对应代码(SomeRecord)

namespace ZZZ_CodeTest.Records
{
    public record SomeRecord
    {
        protected SomeRecord(SomeRecord original)
        {
            _key = null;
            this.Field1 = original.Field1; // 缺失这四行时测试不通过
            this.Field2 = original.Field2;
            this.Field3 = original.Field3;
            this.Field4 = original.Field4;
        }

        private SomeRecordKey? _key;
        public SomeRecordKey Key
        {
            get
            {
                // 可查看文档了解其他重载(包括同步对象)
                LazyInitializer.EnsureInitialized(ref _key, () => new SomeRecordKey(Field1, Field2, Field3));
                return _key;
            }
        }

        public required string Field1 { get; init; }
        public required string Field2 { get; init; }
        public string? Field3 { get; init; }
        public string? Field4 { get; init; }

    }
}

MSTest测试类

namespace ZZZ_CodeTest.Records
{
    [TestClass]
    public sealed class SomeRecordTest
    {
        [TestMethod]
        [DataRow("A", "B", null, null)]
        [DataRow("A", "B", "C", null)]
        [DataRow("A", "B", null, "D")]
        [DataRow("A", "B", "C", "D")]
        public void TestEquals(string field1, string field2, string? field3, string? field4)
        {
            SomeRecord sr1 = new()
            {
                Field1 = field1,
                Field2 = field2,
                Field3 = field3,
                Field4 = field4,
            };

            SomeRecord sr2 = new()
            {
                Field1 = field1,
                Field2 = field2,
                Field3 = field3,
                Field4 = field4,
            };

            Assert.AreEqual(sr1, sr2);
        }

        [TestMethod]
        [DataRow("A", "B", null, null, "E")]
        [DataRow("A", "B", "C", null, "E")]
        [DataRow("A", "B", null, "D", "E")]
        [DataRow("A", "B", "C", "D", "E")]
        public void TestWith(string field1, string field2, string? field3, string? field4, string newField1)
        {
            SomeRecord sr = new()
            {
                Field1 = field1,
                Field2 = field2,
                Field3 = field3,
                Field4 = field4,
            };

            Assert.AreEqual(sr.Field1, sr.Key.Field1);
            Assert.AreEqual(sr.Field2, sr.Key.Field2);
            Assert.AreEqual(sr.Field3, sr.Key.Field3);

            SomeRecord mr = sr with
            {
                Field1 = newField1,
            };

            Assert.AreNotEqual(sr, mr);
            Assert.AreNotEqual(sr.Key, mr.Key);

            Assert.AreNotEqual(sr.Field1, mr.Field1);
            Assert.AreEqual(sr.Field2, mr.Field2);
            Assert.AreEqual(sr.Field3, mr.Field3);
            Assert.AreEqual(sr.Field4, mr.Field4);

            Assert.AreEqual(mr.Field1, newField1);
            Assert.AreEqual(mr.Field1, mr.Key.Field1);
            Assert.AreEqual(mr.Field2, mr.Key.Field2);
            Assert.AreEqual(mr.Field3, mr.Key.Field3);
        }

    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:14:50