享受代码,享受人生

SOA is an integration solution. SOA is message oriented first.
The Key character of SOA is loosely coupled. SOA is enriched
by creating composite apps.
posts - 213, comments - 2305, trackbacks - 162, articles - 45
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

Array & IList (something wrong in Windbey beta2)

Posted on 2005-08-15 20:28 idior 阅读(...) 评论(...) 编辑 收藏

根据Program to interface的原理, 我们应该尽可能的使用接口而不是具体类. 因此在集合操作和算法中我们应该尽可能的使用ICollection<T>和IList<T>.象下面这样

 public void ListAlogrithm<T>(IList<T> list)
        
{
            
//Do something to list
        }

而不是使用List<T> 等等具体的集合类.
但是Windbey Beta2在这里出了一个设计上的问题.
如果你为上面的方法传入一个数组会怎么样?
你显然希望数组T []也实现了IList<T>, 这样就不需要使用一个Adaptor来包装数组才能直接使用上面的方法.附录中有Adaptor的源码.

 class Program
    
{
        
static void Main(string[] args)
        
{
            
int[] intArr = new int[] 12345 };
            ListAlogrithm
<int>(intArr);
        }


        
public static void ListAlogrithm<T>(IList<T> list)
        
{
            
foreach (T t in list)
                Console.Write(t.ToString());
        }

    }

Ok! 看来windbey实现了上面的功能, 使数组也实现了IList<T>的接口, 这样对于集合操作的统一性带来了很大的好处.
但是再看看下面这个例子.

       static void Main(string[] args)
        
{
            
int[] intArr = new int[] 12345 };
            ListAlogrithmRemove
<int>(intArr);
        }

 
public static void ListAlogrithmRemove<T>(IList<T> list)
        
{
            list.RemoveAt(
0);
        }

我在算法中对集合进行了修改(删除了第一个元素), 运行这个例子你会发现它抛出了异常.
Collection was of a fixed size.
如果修改集合的元素呢?

        static void Main(string[] args)
        
{
            
int[] intArr = new int[] 12345 };
            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 的意思.

 

 

 static void Main(string[] args)
        
{
            
int[] intArr = new int[] 12345 };
            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的代码

public struct ArrayToListAdapter<T> : IList<T> 

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