代码之家  ›  专栏  ›  技术社区  ›  Daniel Hanák

C#无参数初始化类属性,用于在ctor中设置字段参数

  •  0
  • Daniel Hanák  · 技术社区  · 2 年前

    我目前正在解决一些我已经解决了至少五次的问题。 让我们上这样的课。这是一个有效的解决方案,但我必须通过ctor中的参数初始化类。

    public class ArrayBenchmark
    {
        private readonly int[] _numbers;
    
        public ArrayBenchmark(uint elementCount)
        {
            _numbers = Enumerable
                .Range(0, (int)elementCount)
                .Select(_ => new Random().Next(-9, 10))
                .ToArray();
        }
    

    如果我想以无参数的方式使用它,它是不起作用的,因为ctor有一定的优先级,所以字段总是用默认的属性值初始化。下面的代码示例。

    public class ArrayBenchmark
    {
        private readonly int[] _numbers;
    
        public uint ElementCount { get; init; } = 1;
    
        public ArrayBenchmark()
        {
            _numbers = Enumerable
                .Range(0, (int)ElementCount)
                .Select(_ => new Random().Next(-9, 10))
                .ToArray();
        }
    
    var benchmarkWith10Elements = new ArrayBenchmark { ElementCount = 10 };
    benchmarkWith10Elements.CalculateAllComplexities();
    

    那么,有没有办法通过无参数类初始化来填充属性中的字段参数呢? 谢谢大家

    2 回复  |  直到 2 年前
        1
  •  2
  •   Tim Schmelter    2 年前

    您想要什么是不可能的,因为在初始化属性之前会调用无参数构造函数。这

    var benchmarkWith10Elements = new ArrayBenchmark { ElementCount = 10 };
    

    是与(的语法糖)相同的代码。。。

    var benchmarkWith10Elements = new ArrayBenchmark(); // here ElementCount is 1
    benchmarkWith10Elements.ElementCount = 10;
    

    所以你需要一个构造函数 ElementCount 参数:

    public ArrayBenchmark(uint elementCount)
    {
        ElementCount = elementCount;
        _numbers = Enumerable
            .Range(0, (int)ElementCount)
            .Select(_ => new Random().Next(-9, 10))
            .ToArray();
    }
    
        2
  •  1
  •   Mushroomator    2 年前

    从代码中我可以看到 无参数类初始化 你的意思是 object initializers 。因此,对于您的用例,不,当您的初始化逻辑依赖于构造函数中的公共属性时,您不能使用对象初始化器。

    原因很简单。对象初始值设定项只是为我们提供的一些语法糖。 SharpLab 我们马上就能看到这一点。

    这里是“正常”C#代码:

    namespace MyProgram;
    using System;
    using System.Linq;
    
    public class Program
    {
        public class ArrayBenchmark
        {
            private readonly int[] _numbers;
            public uint ElementCount { get; init; } = 3;
    
            public ArrayBenchmark()
            {
                _numbers = Enumerable
                    .Range(0, (int)ElementCount)
                    .Select(_ => new Random().Next(-9, 10))
                    .ToArray();
            }
        }
        
        public static void Main(string[] args)
        {
            var benchmarkWith10Elements = new ArrayBenchmark { ElementCount = 10 };
        }
    }
    

    然后,等效的降低的C#将如下所示。我只是在展示 Main() 方法,因为这显示了相关部分。参观 SharpLab 并将正常的C#代码粘贴到中,以查看降低的C#中的完整类。

            [System.Runtime.CompilerServices.NullableContext(1)]
            public static void Main(string[] args)
            {
                ArrayBenchmark arrayBenchmark = new ArrayBenchmark();
                arrayBenchmark.ElementCount = 10u;
                ArrayBenchmark arrayBenchmark2 = arrayBenchmark;
            }
    

    在这里我们可以看到,对象初始值设定项变成了一个普通的构造函数调用,然后是一个属性赋值。因此,在属性获得新值并使用默认值之前,构造函数中的逻辑将已经运行。

    因此,我建议在您的用例中坚持使用构造函数调用。理论上,setter中当然也可以有一些逻辑,但不必要地创建两个数组,setter应该是快速操作,这可能不是因为 ElementCount 可能相当大。因此,虽然理论上可能,但这不是一个好主意。

    推荐文章