我知道什么时候会返回 IEnumerable来自方法——当延迟执行有值(value)时。并返回 List或 IList几乎应该只在结果要被修改时才会出现,否则我会返回一个 IReadOnlyCollection ,所以调用者知道他得到的不是要修改的(这使得该方法甚至可以重用来自其他调用者的对象)。
但是,在参数输入方面,我不太清楚。我可以拿IEnumerable , 但如果我需要多次枚举怎么办?
俗话说“对发送的内容要保守,对接受的内容要自由”建议采取 IEnumerable很好,但我不太确定。
例如,如果以下IEnumerable中没有元素参数,通过检查 .Any() 可以在此方法中节省大量工作首先,这需要 ToList()在此之前避免枚举两次。
public IEnumerable<Data> RemoveHandledForDate(IEnumerable<Data> data, DateTime dateTime) {
var dataList = data.ToList();
if (!dataList.Any()) {
return dataList;
}
var handledDataIds = new HashSet<int>(
GetHandledDataForDate(dateTime) // Expensive database operation
.Select(d => d.DataId)
);
return dataList.Where(d => !handledDataIds.Contains(d.DataId));
}
所以我想知道最好的签名是什么?一种可能性是 IList<Data> data , 但接受一个列表表明你打算修改它,这是不正确的——这个方法没有触及原始列表,所以 IReadOnlyCollection<Data>看起来好多了。
但是IReadOnlyCollection强制调用者做 ToList().AsReadOnly()每次都变得有点难看,即使使用自定义扩展方法也是如此 .AsReadOnlyCollection .这在所接受的方面并不自由。
这种情况下的最佳做法是什么?
此方法不返回 IReadOnlyCollection因为最后的 Where 可能有值(value)使用延迟执行,因为不需要 枚举整个列表。然而,Select需要枚举,因为做 .Contains 的成本没有 HashSet 会很糟糕.
我调用 ToList 没问题,我突然想到,如果我需要 List为了避免多重枚举,为什么我不在参数中只要求一个?所以这里的问题是,如果我不想要 IEnumerable在我的方法中,我是否真的应该接受一个以变得自由(和 ToList 我自己),或者我应该把负担放在调用者身上 ToList().AsReadOnly() ?
为不熟悉 IEnumerables 的人提供的更多信息
这里真正的问题不是 Any() 的成本与 ToList() .我知道枚举整个列表比做 Any() 花费更多.但是,假设调用者将消耗返回中的所有项目 IEnumerable从上面的方法,并假设来源IEnumerable<Data> data参数来自此方法的结果:
public IEnumerable<Data> GetVeryExpensiveDataForDate(DateTime dateTime) {
// This query is very expensive no matter how many rows are returned.
// It costs 5 seconds on each `.GetEnumerator` call to get 1 value or 1000
return MyDataProvider.Where(d => d.DataDate == dateTime);
}
现在如果你这样做:
var myData = GetVeryExpensiveDataForDate(todayDate);
var unhandledData = RemoveHandledForDate(myData, todayDate);
foreach (var data in unhandledData) {
messageBus.Dispatch(data); // fully enumerate
)
如果RemovedHandledForDate做 Any 和做Where ,您将承担 5 秒的费用两次,而不是一次。这就是为什么您应该始终竭尽全力避免枚举 IEnumerable不止一次。不要相信它实际上是无害的知识,因为 future 某个倒霉的开发人员可能有一天会用新实现的 IEnumerable 调用您的方法。你想不到的,它有不同的特点。
IEnumerable 的契约(Contract)说你可以枚举它。它不 promise 多次这样做的性能特征。
事实上,一些IEnumerables volatile 并且不会在后续枚举时返回任何数据!如果与多重枚举相结合,切换到一个将是一个完全破坏性的变化(如果稍后添加多重枚举,则很难诊断)。
不要对 IEnumerable 进行多次枚举。
如果您接受一个 IEnumerable 参数,您实际上是在 promise 将它精确枚举 0 次或 1 次。
最佳答案
IReadOnlyCollection<T>添加到 IEnumerable<T>一个Count属性(property)和相应的 promise ,即没有延迟执行。如果该参数是您想要解决此问题的地方,这将是要求的适当参数。
但是,我建议询问 IEnumerable<T> , 并调用 ToList()而不是在实现本身中。
观察:这两种方法都有一个缺点,即多重枚举可能在某些时候被重构掉,呈现参数变化或 ToList()称为冗余,我们可能会忽略这一点。我认为这是无法避免的。
这个案例确实可以调用ToList()在方法体中:由于多重枚举是一个实现细节,因此避免它也应该是一个实现细节。这样,我们就避免了影响 API。如果多重枚举被重构掉,我们也避免更改回 API。我们还避免通过一系列方法传播需求,所有这些方法都必须请求 IReadOnlyCollection<T>。只是因为我们的多重枚举。
如果您担心创建额外列表的开销(当输出已经是一个列表左右时),Resharper 建议采用以下方法:
param = param as IList<SomeType> ?? param.ToList();
当然,我们可以做得更好,因为我们只需要防止延迟执行 - 不需要成熟的 IList<T> :
param = param as IReadOnlyCollection<SomeType> ?? param.ToList();
关于c# - 参数 : IEnumerable vs. IList 与 IReadOnlyCollection 的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33637294/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha