Array & IList (something wrong in Windbey beta2)
Posted on 2005-08-15 20:28 idior 阅读(4804) 评论(4) 编辑 收藏 举报根据Program to interface的原理, 我们应该尽可能的使用接口而不是具体类. 因此在集合操作和算法中我们应该尽可能的使用ICollection<T>和IList<T>.象下面这样
{
//Do something to list
}
而不是使用List<T> 等等具体的集合类.
但是Windbey Beta2在这里出了一个设计上的问题.
如果你为上面的方法传入一个数组会怎么样?
你显然希望数组T []也实现了IList<T>, 这样就不需要使用一个Adaptor来包装数组才能直接使用上面的方法.附录中有Adaptor的源码.
{
static void Main(string[] args)
{
int[] intArr = new int[] { 1, 2, 3, 4, 5 };
ListAlogrithm<int>(intArr);
}
public static void ListAlogrithm<T>(IList<T> list)
{
foreach (T t in list)
Console.Write(t.ToString());
}
}
Ok! 看来windbey实现了上面的功能, 使数组也实现了IList<T>的接口, 这样对于集合操作的统一性带来了很大的好处.
但是再看看下面这个例子.
{
int[] intArr = new int[] { 1, 2, 3, 4, 5 };
ListAlogrithmRemove<int>(intArr);
}
public static void ListAlogrithmRemove<T>(IList<T> list)
{
list.RemoveAt(0);
}
我在算法中对集合进行了修改(删除了第一个元素), 运行这个例子你会发现它抛出了异常.
Collection was of a fixed size.
如果修改集合的元素呢?
{
int[] intArr = new int[] { 1, 2, 3, 4, 5 };
ListAlogrithmModify<int>(intArr,6);
}
public static void ListAlogrithmModify<T>(IList<T> list, T t)
{
list[0] = t;
}
也会抛出异常. Collection is read-only.
现在你发现这个设计上的问题了. 当Array被Cast成IList<T>的时候变成只读的了. 你既不能添加删除元素,又不能修改已有的元素. 当然数组不能添加删除元素很正常, 不过不能修改内容就不太合理了.这时你不得不为Array做一个Adaptor才能在算法中对元素进行修改. 这就是beta2中的一个有关集合设计存在的问题, 不过幸而BCL小组认识到了这点, 宣布在wnidbey正式发布的时候有望解决此问题.
但是他们对另一个问题却也无法挽救了. 看看前面的两个异常.
Collection was of a fixed size. Collection is read-only.
集合中涉及两种操作:
1. 为集合添加删除元素
2. 修改集合中的某些元素.
对应了IList中的两个属性:
1. IsFixedSize 2. IsReadOnly
但是在IList<T>(IList的泛型实现)中却把IsFixedSize 给去掉了,只保留了IsReadOnly .设计者可能是出于简化的考虑.因为一般情况下固定大小的集合同时也不能修改其中的元素. 但是对于Array来说这显然是一个失策的决定. 因为Array可以修改集合元素,而集合的大小确是固定的.
等到将来正式版允许数组修改元素了,看看下面的代码.你是不是对IsReadOnly 的含义很困惑. 因为这里的IsReadOnly 已经变成了IsFixedSize 的意思.
{
int[] intArr = new int[] { 1, 2, 3, 4, 5 };
ListAlogrithmModify<int>(intArr,6);
ListAlogrithmRemove<int>(intArr);
}
public static void ListAlogrithmModify<T>(IList<T> list, T t)
{
//if (list.IsReadOnly == false) //似乎这样很合理,但是IsReadOnly 的含义其实是IsfixedSize所以不该用在这里
list[0] = t;
}
public static void ListAlogrithmRemove<T>(IList<T> list)
{
if (list.IsReadOnly == false) //Array is a fixedsize, so here is ture, u can't remove anymore
list.RemoveAt(0);
}
既然语义这么不清晰为什么不改? BCL小组说了现在往IList<T>中加入IsFixedSize 属性已经不太可能了,代价太大.
个人认为PowerCollection的作者对此提出的建议比较好.
把IsFixedSize 重命名为CanAddRemove, 再把IsReadOnly 改为CanChangeValues.
read/write List<T>: CanAddRemove=true, CanChangeValues=true
read/write array: CannAddRemove=false, CanChangeValues=true
read-only List<T>: CanAddRemove=false, CanChangeValues=false
OrderedSet<T>: CanAddRemove=true, CanChangeValues=false
附: Array Adapter成IList的代码
{
private T[] array;
public ArrayToListAdapter(T[] array)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
this.array = array;
}
public bool IsReadOnly
{
get { return false; }
}
public int Count
{
get { return this.array.Length; }
}
public T this[int index]
{
get { return this.array[index]; }
set { this.array[index] = value; }
}
public int IndexOf(T item)
{
return Array.IndexOf<T>(this.array, item);
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
public bool Contains(T item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(T[] array, int arrayIndex)
{
Array.Copy(this.array, 0, array, arrayIndex, this.array.Length);
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)this.array).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.array.GetEnumerator();
}
}
public static class Adapters
{
public static ArrayToListAdapter<T> ArrayToList<T>(T[] array)
{
return new ArrayToListAdapter<T>(array);
}
}
参考资料: NET Arrays, IList<T>, Generic Algorithms, and what about STL? [Brian Grunkemeyer]
IList, ICollection and IsReadOnly