C# 方法生成未从构造函数调用的返回?

C# 方法生成未从构造函数调用的返回?,c#,C#,我在理解C#的一些语法和内部工作方面遇到了困难。我有一个在构造函数中设置了只读属性栏的类 如果我使用InitializeWithYield方法执行该操作,并且yield返回一些东西,则该方法不会被称为构造函数。而是在每次调用(!)属性时调用它。不是我所期望的 如果我使用“plain”方法,它会像我预期的那样工作:初始化属性一次,然后完成 显然,我不完全理解yield的用法。我的问题是:为什么不是从构造函数调用InitializeWithYield方法,而是每次调用属性时都调用该方法 class

我在理解C#的一些语法和内部工作方面遇到了困难。我有一个在构造函数中设置了只读属性栏的类

如果我使用
InitializeWithYield
方法执行该操作,并且
yield返回一些东西,则该方法不会被称为构造函数。而是在每次调用(!)属性时调用它。不是我所期望的

如果我使用“plain”方法,它会像我预期的那样工作:初始化属性一次,然后完成

显然,我不完全理解
yield
的用法。我的问题是:为什么不是从构造函数调用
InitializeWithYield
方法,而是每次调用属性时都调用该方法

class Foo
{
    public Foo(int length)
    {
        // toggle these to see the issue
        Bars = InitializeWithYield(length);
        //Bars = InitializeCalledFromConstructor(length);
    }

    public void DoSomething()
    {
        IBar firstBar = Bars.FirstOrDefault(); // calls InitializeWithYield if in constructor 
        IList<IBar> listOfBar = Bars.ToList(); // calls InitializeWithYield if in constructor
        Console.Write($"First Bar has value {b.Value}");
    }

    IEnumerable<IBar> Bars { get; }

    // shorter syntax, but gets called every time the Bars property is used
    // also, not called from constructor
    IEnumerable<IBar> InitializeWithYield(int number)
    {
        for (int i = 0; i < number; i++)
        {
            yield return new Bar(i);
        }
    }

    // this only gets called from the constructor, which is what I want
    IEnumerable<IBar> InitializeCalledFromConstructor(int number)
    {
        IList<IBar> result = new List<IBar>();
        for (int i = 0; i < number; i++)
        {
            result.Add(new Bar(i));
        }
        return result;
    }
}
class-Foo
{
公共Foo(整数长度)
{
//切换这些选项以查看问题
棒材=初始化屈服强度(长度);
//Bars=从构造函数初始化CalledFromConstructor(长度);
}
公共无效剂量测定法()
{
IBar firstBar=Bars.FirstOrDefault();//在构造函数中调用InitializeWithYield
IList listOfBar=Bars.ToList();//在构造函数中调用InitializeWithYield
Write($“第一个条有值{b.value}”);
}
IEnumerable条{get;}
//较短的语法,但每次使用Bars属性时都会被调用
//另外,不是从构造函数调用的
IEnumerable InitializeWithYield(整数)
{
for(int i=0;i
这是延迟执行,延迟计算。请看

这是一篇很好的文章,解释了延迟执行和急切与懒惰的评估

我鼓励你阅读整篇短文,但这里有两段引语

这里有一个关于延迟执行的例子:

延迟执行意味着表达式的计算将被延迟,直到实际需要它的实现值

下面是一个关于急切与懒惰评估的例子:

  • 在惰性计算中,在每次调用迭代器期间,都会处理源集合的单个元素。这是实现迭代器的典型方式
  • 在即时求值中,对迭代器的第一次调用将导致处理整个集合。可能还需要源集合的临时副本。例如,OrderBy方法必须在返回第一个元素之前对整个集合进行排序

下面是另一个显示
IEnumerable
惰性的示例

    public static IEnumerable<DateTime> GetNow()
    {
        while (true) { yield return DateTime.Now; }
    }


    public static async Task Main()
    {
        foreach(var now in GetNow())
        {
            Console.WriteLine(now);
            await Task.Delay(TimeSpan.FromSeconds(1));
        }
    }

正如您的代码所证明的,枚举数的行为与普通数组完全不同。它们的值是延迟加载或按需加载的。这不同于一次加载所有值的普通数组

因此,会为每个未初始化的值调用
InitializeWithYield()


这里有一个快速/简短的答案,但我建议您在


c#中的yield/return语句会导致在后台构建一个状态机,每次访问该值时都会对集合进行导航。指针会被维护,每次访问访问器时,都会返回下一个值。

调用
.ToList()
.ToArray()
on
InitializeWithYield(length)
以实际获取迭代器方法返回的项目-在实际迭代之前不会执行这些项目(ToList/ToArray就是这样做的). 我建议使用调试器或打印输出来逐步了解这些方法是如何运行的。也许这里更好的方法是不提供
Bars
属性,而是在任何地方使用
InitializeWithYield
方法,重命名为
Bars
,并使用“\u length”作为类成员,但我不知道这个示例类的实际含义。谢谢。我知道你建议添加
.ToList()
可以解决我的问题,但老实说,我并不完全理解。为什么
初始化为不产生
“simply”运行?我可能应该更深入地研究“迭代器方法”。使用迭代器函数的一个好例子是使用自定义逻辑过滤列表。调用方可以迭代迭代器函数逐个元素返回的集合,直到找到所需的项,然后在迭代整个集合之前中止迭代。然后,不需要为可能已返回的整个列表保留内存。事实上,
System.Linq
方法的工作原理与此完全相同。事实上,这正是我在实际应用程序中所做的,只是我在初始化要使用的集合时措手不及。您确实需要将要筛选的基集合存储在任何位置。这通常位于类型为
ICollection
IList
的属性中<代码>IEnumerable
不是存储的集合。这是唯一可以根据要求列举的东西。我认为,如果您通过将属性更改为
IList Bars
来明确这一意图,您还将看到,您不能仅仅将迭代器方法的“结果”分配给它,因为这只是一个
IEnumerable
20/10/2019 08:00:45
20/10/2019 08:00:46
20/10/2019 08:00:47
20/10/2019 08:00:48
20/10/2019 08:00:49