如何解决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
简而言之,有两种方法:
- 命令,例如
void DoSomething(string data)
-它们会更改您实例的状态 - 查询,例如
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)
不会枚举其返回的集合或迭代器。
-
它可能返回
var values = DoSomethingX();
。在这种情况下,该列表已分配给List<int>
。更准确地说:对此列表的引用。 -
如果
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 举报,一经查实,本站将立刻删除。