有时Resharper会警告:
可能的多个枚举IEnumerable
关于如何处理这个问题还有一个问题,ReSharper网站也在这里解释了一些事情.它有一些示例代码,告诉您这样做:
IEnumerable<string> names = GetNames().ToList();
Run Code Online (Sandbox Code Playgroud)
我的问题是关于这个特定的建议:这不会导致在2 for-each循环中两次枚举集合吗?
在.NET 4.5/C#5中,IReadOnlyCollection<T>使用Count属性声明:
public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
int Count { get; }
}
Run Code Online (Sandbox Code Playgroud)
我想知道,ICollection<T>实现IReadOnlyCollection<T>接口也没有意义:
public interface ICollection<T> : IEnumerable<T>, IEnumerable, *IReadOnlyCollection<T>*
Run Code Online (Sandbox Code Playgroud)
这意味着实现的类ICollection<T>将自动实现IReadOnlyCollection<T>.这对我来说听起来很合理.
的ICollection<T>抽象可以被看作是所述的扩展IReadOnlyCollection<T>抽象.请注意List<T>,例如,实现两者ICollection<T>和IReadOnlyCollection<T>.
然而,它并没有这样设计.
我在这里错过了什么?为什么要选择当前的实现呢?
UPDATE
我正在寻找一个使用面向对象设计推理来解释原因的答案:
List<T>实现IReadOnlyCollection<T> 和 ICollection<T>是一个比以下更好的设计:
ICollection<T>IReadOnlyCollection<T>直接实施另请注意,这基本上与以下问题相同:
IList<T>实施IReadOnlyList<T>?IDictionary<T>实施IReadOnlyDictionary<T>?我想要得到的想法,这将是发布对象的只读目录作为一个公共方法的最佳方式?从Eric Lippert的博客来看,阵列有点糟糕,因为有人可以轻松添加新条目.因此,每次调用该方法时都必须传递一个新数组.他建议,要通过IEnumerable<T>,因为这是每个定义只读(没有添加,删除方法),我练习了很长一段时间.但是在我们的新项目中,人们甚至开始创建这些的数组IEnumerables,因为他们不知道后面的DataSource,所以他们得到:处理警告可能多次枚举IEnumerable
我对技术方法感兴趣,如何解决这个难题.到目前为止,我提出的唯一解决方案是使用a IReadOnlyCollection,但这比使用更明确IEnumerable.
发布此类列表的最佳做法是什么,这些列表不应更改,但应声明为内存列表?
关于"可能存在多个枚举"的SO有一个问题,但这个问题更具体.
请考虑以下方法,该方法接受IEnumerable<string>输入并对其每个元素执行给定方法:
public static bool SomeMethod(IEnumerable<string> enumerable)
{
if (enumerable.IsNullOrEmpty())
{
// throw exception.
}
else
{
return (enumerable.All(SomeBooleanMethod));
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,IsNullOrEmpty只是一个运行的扩展方法
return (!ReferenceEquals(enumerable, null) || enumerable.Any());
Run Code Online (Sandbox Code Playgroud)
问题是ReSharper警告我"IEnumerable的可能的多个枚举",我真的不知道这是否真的是一个问题.
我理解警告的含义,但是如果你真的需要在无效或空洞的情况下检查并抛出异常,你在这种情况下真的可以做些什么?
我的问题与这个关于使用IEnumerable<T>vs的问题有关IReadOnlyCollection<T>.
我也经常习惯IEnumerable<T>将集合作为返回类型和参数公开,因为它既可以是不可变的,也可以是懒惰的.
但是,我越来越关注代码中地点的扩散,我必须枚举一个参数以避免ReSharper给出的可能的多个枚举警告.我理解为什么ReSharper建议这一点,我同意它建议的代码(下面)以确保封装(即,没有关于调用者的假设).
Foo[] arr = col as Foo[] ?? col.ToArray();
Run Code Online (Sandbox Code Playgroud)
但是,我发现此代码的重复性是污染性的,我同意一些IReadOnlyCollection<T>更好的替代方案,特别是本文中提出的观点,其中指出:
最近,我一直在考虑回归的优点和缺点
IEnumerable<T>.从积极的一面,它大约是最小的一个接口获得,所以给人们留下你的方法作者比承诺更重的替代需要更多的灵活性
IList<T>或(但愿)阵列.但是,正如我在上一篇文章中所概述的那样,
IEnumerable<T>回归诱使来电者违反Liskov替代原则.这太容易让他们使用LINQ扩展方法,如Last()和Count(),其语义IEnumerable<T>不答应.我们需要的是一种更好的方法来锁定一个返回的集合,而不会让这种诱惑如此突出.(我想起Barney Fife艰难地学习这一课.)
IReadOnlyCollection<T>在.NET 4.5中输入new.它只添加一个属性IEnumerable<T>:Count属性.通过承诺计数,你向你的来电者保证你IEnumerable<T>确实有一个终点.然后,他们可以使用LINQ扩展方法,如Last()清醒的良心.
但是,正如观察者可能已经注意到的那样,本文仅讨论使用IReadOnlyCollection<T>返回类型.我的问题是,相同的论点同样适用于将其用于参数吗?对此的任何理论思考或评论也将受到赞赏.
事实上,我认为使用的一般经验法则IReadOnlyCollection<T>是,如果使用的话,可能存在多次枚举(相对于ReSharper警告)IEnumerable<T>.否则,请使用IEnumerable<T>.
我的问题类似于前一个问题,但这个问题的答案不适用于此问题.
好吧,我想为两者IDictionary和IReadOnlyDictionary接口编写扩展方法:
public static TValue? GetNullable<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
where TValue : struct
{
return dictionary.ContainsKey(key)
? (TValue?)dictionary[key]
: null;
}
public static TValue? GetNullable<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
where TValue : struct
{
return dictionary.ContainsKey(key)
? (TValue?)dictionary[key]
: null;
}
Run Code Online (Sandbox Code Playgroud)
但是当我将它用于实现两个接口的类(例如Dictionary<Tkey, TValue>)时,我得到了"模糊的调用".我不想打字var value = myDic.GetNullable<IReadOnlyDictionary<MyKeyType, MyValueType>>(key),我希望它只是var value = myDic.GetNullable(key).
这可能吗?
当我需要执行以下操作时,我试图找出 LINQ 的正确约定是什么
"No items"我想这样做的方式就像
if (items.Any())
{
foreach (string item in items)
{
Console.WriteLine(item);
}
}
else
{
Console.WriteLine("No items");
}
Run Code Online (Sandbox Code Playgroud)
然而,这在技术上违反了多重枚举原则。一种不违反这一点的方法是
bool any = false;
foreach (string item in items)
{
any = true;
Console.WriteLine(item);
}
if (!any)
{
Console.WriteLine("No items");
}
Run Code Online (Sandbox Code Playgroud)
但显然,这不太优雅。
C#不允许lambda函数表示迭代器块(例如,lambda函数内不允许"yield return").如果我想创建一个懒惰的枚举,例如在枚举时产生所有驱动器,我想做类似的事情
IEnumerable<DriveInfo> drives = {foreach (var drive in DriveInfo.GetDrives())
yield return drive;};
Run Code Online (Sandbox Code Playgroud)
花了一段时间,但我认为这是获得该功能的一种方式:
var drives = Enumerable.Range(0, 1).SelectMany(_ => DriveInfo.GetDrives());
Run Code Online (Sandbox Code Playgroud)
有更惯用的方式吗?
我写了以下代码:
IEnumerable<string> blackListCountriesCodes =
pair.Criterion.CountriesExceptions.Select(countryItem => countryItem.CountryCode);
IEnumerable<string> whiteListCountriesCodes =
pair.Criterion.Countries.Select(countryItem => countryItem.CountryCode);
return (!blackListCountriesCodes.Contains(Consts.ALL.ToString()) &&
!blackListCountriesCodes.Contains(country) &&
(whiteListCountriesCodes.Contains(Consts.ALL.ToString()) ||
whiteListCountriesCodes.Contains(country)));
Run Code Online (Sandbox Code Playgroud)
resharper给我一个警告:
Possible duplicate enumeration of IEnumerable
这是什么意思?为什么这是一个警告?
c# ×10
resharper ×5
linq ×3
.net-4.5 ×2
ienumerable ×2
.net ×1
.net-4.0 ×1
algorithm ×1
arrays ×1
c#-5.0 ×1
enumeration ×1
idictionary ×1
iterator ×1
lambda ×1
oop ×1