4

好的,我的实际问题是:我正在实施一个IList<T>. 当我到达时CopyTo(Array array, int index),这是我的解决方案:

void ICollection.CopyTo(Array array, int index)
{
    // Bounds checking, etc here.
    if (!(array.GetValue(0) is T))
        throw new ArgumentException("Cannot cast to this type of Array.");
    // Handle copying here.
}

这在我的原始代码中有效,并且仍然有效。但它有一个小缺陷,直到我开始为它构建测试时才暴露出来,特别是这个:

public void CopyToObjectArray()
{
    ICollection coll = (ICollection)_list;
    string[] testArray = new string[6];

    coll.CopyTo(testArray, 2);
}

现在,这个测试应该通过了。它抛出ArgumentException关于无法投射的问题。为什么?array[0] == null. 检查设置为 的变量时,is关键字始终返回 false null。现在,出于各种原因,这很方便,包括避免 null 取消引用等。我最终想出的类型检查是这样的:

try
{
    T test = (T)array.GetValue(0);
}
catch (InvalidCastException ex)
{
    throw new ArgumentException("Cannot cast to this type of Array.", ex);
}

这并不完全优雅,但它有效......有没有更好的方法?

4

4 回答 4

4

Type 上有一个专门为此而设计的方法,试试:

if(!typeof(T).IsAssignableFrom(array.GetElementType()))
于 2008-10-04T02:18:23.697 回答
3

唯一可以确定的方法是使用反射,但 90% 的时间你可以通过使用array is T[]. 大多数人会传入一个正确类型的数组,这样就可以了。但是,您也应该始终提供代码来进行反射检查,以防万一。这是我的一般样板的样子(注意:我在这里写了这个,从内存中,所以这可能无法编译,但它应该给出基本的想法):

class MyCollection : ICollection<T> {
   void ICollection<T>.CopyTo(T[] array, int index) {
       // Bounds checking, etc here.
       CopyToImpl(array, index);
   }
   void ICollection.CopyTo(Array array, int index) {
       // Bounds checking, etc here.
       if (array is T[]) { // quick, avoids reflection, but only works if array is typed as exactly T[]
           CopyToImpl((T[])localArray, index);
       } else {
           Type elementType = array.GetType().GetElementType();
           if (!elementType.IsAssignableFrom(typeof(T)) && !typeof(T).IsAssignableFrom(elementType)) {
               throw new Exception();
           }
           CopyToImpl((object[])array, index);
       }
   }
   private void CopyToImpl(object[] array, int index) {
       // array will always have a valid type by this point, and the bounds will be checked
       // Handle the copying here
   }
}

编辑:好的,忘了指出一些事情。几个答案天真地使用了在此代码中读取的内容element.IsAssignableFrom(typeof(T))。您还应该像 BCL 那样允许typeof(T).IsAssignableFrom(elementType),以防开发人员知道此特定ICollection中的所有值实际上都是S派生自的类型T,并传递一个类型的数组S[]

于 2008-10-04T02:47:33.183 回答
1

List<T>使用这个:

try
{
    Array.Copy(this._items, 0, array, index, this.Count);
}
catch (ArrayTypeMismatchException)
{
  //throw exception...
}
于 2008-10-04T02:31:18.647 回答
0

这是 try / catch 与反射的一个小测试:

object[] obj = new object[] { };
DateTime start = DateTime.Now;

for (int x = 0; x < 1000; x++)
{
    try
    {
        throw new Exception();
    }
    catch (Exception ex) { }
}
DateTime end = DateTime.Now;
Console.WriteLine("Try/Catch: " + (end - start).TotalSeconds.ToString());

start = DateTime.Now;

for (int x = 0; x < 1000; x++)
{
    bool assignable = typeof(int).IsAssignableFrom(obj.GetType().GetElementType());
}
end = DateTime.Now;
Console.WriteLine("IsAssignableFrom: " + (end - start).TotalSeconds.ToString());

发布模式下的结果输出是:

Try/Catch: 1.7501001
IsAssignableFrom: 0

在调试模式下:

Try/Catch: 1.8171039
IsAssignableFrom: 0.0010001

结论,只需进行反射检查。这很值得。

于 2009-02-13T14:49:29.550 回答