一种选择是添加自己的扩展方法,该方法返回一个只读的ICollection<T>。当您既不想使用数组/列表的索引属性,也不想从列表中添加/删除时,这可能比使用ToList或ToArray更好。
public static class EnumerableExtension
{
/// <summary>
/// Causes immediate evaluation of the linq but only if required.
/// As it returns a readonly ICollection, is better than using ToList or ToArray
/// when you do not want to use the indexing properties of an IList, or add to the collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="enumerable"></param>
/// <returns>Readonly collection</returns>
public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
{
//if it's already a readonly collection, use it
var collection = enumerable as ICollection<T>;
if ((collection != null) && collection.IsReadOnly)
{
return collection;
}
//or make a new collection
return enumerable.ToList().AsReadOnly();
}
}
单元测试:
[TestClass]
public sealed class EvaluateLinqTests
{
[TestMethod]
public void EvalTest()
{
var list = new List<int> {1, 2, 3};
var linqResult = list.Select(i => i);
var linqResultEvaluated = list.Select(i => i).Evaluate();
list.Clear();
Assert.AreEqual(0, linqResult.Count());
//even though we have cleared the underlying list, the evaluated list does not change
Assert.AreEqual(3, linqResultEvaluated.Count());
}
[TestMethod]
public void DoesNotSaveCreatingListWhenHasListTest()
{
var list = new List<int> {1, 2, 3};
var linqResultEvaluated = list.Evaluate();
//list is not readonly, so we expect a new list
Assert.AreNotSame(list, linqResultEvaluated);
}
[TestMethod]
public void SavesCreatingListWhenHasReadonlyListTest()
{
var list = new List<int> {1, 2, 3}.AsReadOnly();
var linqResultEvaluated = list.Evaluate();
//list is readonly, so we don't expect a new list
Assert.AreSame(list, linqResultEvaluated);
}
[TestMethod]
public void SavesCreatingListWhenHasArrayTest()
{
var list = new[] {1, 2, 3};
var linqResultEvaluated = list.Evaluate();
//arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
Assert.AreSame(list, linqResultEvaluated);
}
[TestMethod]
[ExpectedException(typeof (NotSupportedException))]
public void CantAddToResultTest()
{
var list = new List<int> {1, 2, 3};
var linqResultEvaluated = list.Evaluate();
Assert.AreNotSame(list, linqResultEvaluated);
linqResultEvaluated.Add(4);
}
[TestMethod]
[ExpectedException(typeof (NotSupportedException))]
public void CantRemoveFromResultTest()
{
var list = new List<int> {1, 2, 3};
var linqResultEvaluated = list.Evaluate();
Assert.AreNotSame(list, linqResultEvaluated);
linqResultEvaluated.Remove(1);
}
}