微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C#:迭代器vs函数返回IEnumerable

如何解决C#:迭代器vs函数返回IEnumerable

我们假设有两种方法

IEnumerable<int> DoSomething1();
IEnumerable<int> DoSomething2();

修改我对象的状态。我不知道该函数是在内部使用yield还是仅返回List<int>

然后我想获取输出并传递给其他两个函数

void SendToUser(IEnumerable<int> values);
void PrintOut(IEnumerable<int> values);

然后仅基于功能接口(DoSomethingX)我不能说这是否是有效操作:

var values = DoSomethingX();
SendToUser(values);
PrintOut(values)

由于使用迭代器,将导致两次调用DoSomethingX

这是某种不一致还是我以错误的方式使用了迭代器/ IEnumerable?问题出在哪里?

解决方法

如果某个方法返回IEnumerable,则您仅应依赖于它是可迭代的事实,仅此而已,因为该方法的实现可能会发生变化。

如果您可以控制该方法,并且知道使用者需要一个更特定的类型,则返回一个更特定的类型。

如果您没有控制权并且需要执行列表行为,则可以随时将IEnumerable转换为列表:

var values = DoSomethingX().ToList();
SendToUser(values);
PrintOut(values);
,

了解有关CQS(命令查询分离)的信息:Wikipedia

简而言之,有两种方法:

  1. 命令,例如void DoSomething(string data)-它们会更改您实例的状态
  2. 查询,例如IEnumerable<Person> GetPeople(Filter filter)-不更改状态,它们只是返回一些数据

有时命令可以返回值,即PersonID CreatePerson(...);

因此,如果您将代码重构为:

void DoSomething1();
void DoSomething2();
IEnumerable<int> GetData();

它将解决您的问题。

IEnumerable类型只是一个允许您迭代某些元素的接口。你不知道具体实现里面有什么。
通常,IEnumerable的实现不应有任何副作用,即更改某些对象的状态。

如果要处理更改状态的IEnumerable,则始终可以使用.ToList() LINQ扩展来实现它。然后,您可以将其传递给没有任何其他副作用的任何方法。

,

分配ERROR: Unexpected exception occurred while performing safe-restart command. hudson.lifecycle.RestartNotSupportedException: Default Windows lifecycle does not support restart. at hudson.lifecycle.Lifecycle$1.verifyRestartable(Lifecycle.java:84) at jenkins.model.Jenkins.restartableLifecycle(Jenkins.java:4305) at jenkins.model.Jenkins.safeRestart(Jenkins.java:4339) at jenkins.model.Jenkins.doSafeRestart(Jenkins.java:4294) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at hudson.cli.declarative.MethodBinder.call(MethodBinder.java:114) at hudson.cli.declarative.CLIRegisterer$1.main(CLIRegisterer.java:218) at hudson.cli.CLIAction$ServerSideImpl.run(CLIAction.java:277) at hudson.cli.CLIAction$1.lambda$opened$0(CLIAction.java:148) at java.lang.Thread.run(Unknown Source) 不会枚举其返回的集合或迭代器。

  1. 它可能返回var values = DoSomethingX();。在这种情况下,该列表已分配给List<int>。更准确地说:对此列表的引用。

  2. 如果values是一个迭代器方法,则C#创建一个将DoSomethingX实现为状态机的匿名类的对象。该对象已分配给IEnumerable<int>

因此,这两个values的用法没有区别。它是基于集合还是基于迭代器方法都没关系。迭代通常在for-each循环中开始,或者在调用诸如IEnumerable<int>ToList之类的LINQ方法时开始。

您可以手动进行迭代。首先,您必须获得枚举数:

ToArray

然后调用此枚举器的方法:

IEnumerator<int> enumerator = values.GetEnumerator();

当您对while (enumerator.MoveNext()) { // Enumeration starts here. int value = enumerator.Current; ... } IEnumerable进行两次迭代时,它的代码当然会运行两次。如果您认为这可能很耗时,例如如果迭代器查询数据库或文件系统,则应在两次调用之前先调用IEnumerable<T>

.ToList()

另一方面,当枚举数基于集合时调用var records = QueryDatabaseReturnsIEnumerable().ToList(); SendToUser(records); PrintOut(records); ,则您将执行不必要的内存和耗时的复制操作。

通常,您可以从上下文中说这是否必要。否则,您最好的办法就是测试其他接口:

.ToList()

但是我怀疑这是一个好习惯。应该使用接口而不必担心其实现。这样做违背了他们的目的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。