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