最近我有一个任务要处理其中一个FxCop警告 - 不要暴露通用列表。所以我尝试将List<T>
改为ICollection<T>
。但是稍后的时间点,在进行单元测试时,我发现AddRange()
没有按预期正常工作。它不是将集合元素添加到集合对象中。
这是示例代码
gc.ToList().AddRange(sampleList);
我有两个问题要问
public class GenericClass
{
public int Id;
public string Name;
}
class Program
{
static void Main(string[] args)
{
ICollection<GenericClass> gc = new List<GenericClass>();
var sampleList = new List<GenericClass>()
{
new GenericClass {Id = 1, Name = "ASD"},
new GenericClass {Id = 2, Name = "QWER"},
new GenericClass {Id = 3, Name = "BNMV"},
};
Console.WriteLine(gc.GetType()); // gc is of type
gc.ToList().AddRange(sampleList); // sampleList items are not getting added to gc.
Console.ReadKey();
}
}List<T>
继承自ICollection<T>
和List<T>
有AddRange()
等功能。当我试图将父级引用(ICollection<T>
)转换为子类对象(List<T>
)时,为什么Intellisense不建议使用AddRange()
。相反,我需要做.ToList()
然后它显示AddRange()
。 我搜索了很多。但找不到让我满意的理由。所以请帮助我理解。这将是一个很大的帮助。
AddRange()
方法工作正常。问题是,你没有仔细阅读ToList()
扩展方法的文档,因此没有意识到ToList()
方法返回一个全新的对象。
从IEnumerable <T>创建List <T>。
由于您调用AddRange()
的对象实际上不是原始集合,因此原始集合保持不变。
从某种意义上说,你的问题是List<T>
相当于一个非常常见的问题“为什么string.Replace()
不工作?”
在你给出的例子中,没有比仅仅隐藏List<T>
更好的解决方案。你可以,因为你使用通用的ICollection<T>
接口,编写自己的AddRange()
作为扩展方法:
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> range)
{
foreach (T t in range)
{
collection.Add(t);
}
}
但我不相信这比仅仅将类型保留为List<T>
要好得多,如果目标是能够修改对象并使用AddRange()
方法。
将ICollection<T>
引用转回其底层的List<T>
(正如另一个回答者提议的那样)是毫无意义的,因为这样做会否定在使用ICollection<T>
接口时的任何价值。
只需将引用保留为List<T>
,至少在您确实需要修改集合的任何上下文中。 (只使用ICollection<T>
在其他环境中公开该列表,这很好,甚至可能是有益的,但这是一个完全不同的讨论。)
只要我回答,我就会提到我怀疑你是否正确理解了FxCop警告的意图,因为暴露通用列表本身并不具有本质上的危害。
ToList()
返回一个新实例,这就是为什么你无法在“gc”中看到AddRange的结果。如果您确实想使用AddRange,则可以执行以下操作。
((List<GenericClass>)gc).AddRange(sampleList);