2008-10-04 61 views
4

好吧,我的实际问题是这样的:我实施了IList<T>。当我到CopyTo(Array array, int index),这是我的解决方案:在C#中正确的可空类型检查?

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。检查设置为null的变量时,is关键字始终返回false。现在,这是非常方便的各种原因,包括避免空指针引用,等我终于想出了我的类型检查是这样的:

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

这不完全是优雅,但它的工作原理.. 。还有更好的方法吗?

回答

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))。您应该也让typeof(T).IsAssignableFrom(elementType),为BCL确实,如果一个开发人员都知道,所有在此特定ICollection值都是从T派生的类型S实际上,并将类型的数组S[]

4

上有专门针对此类型的方法,尝试:

if(!typeof(T).IsAssignableFrom(array.GetElementType())) 
+0

使用反射肯定会比无效投射更昂贵。 – 2008-10-04 02:19:20

+0

+1努力,以及一个有效的解决方案。虽然表现明智,但我认为我必须与ffpf站在一边,Reflection可能需要漫长的路线。 – 2008-10-04 02:24:10

+0

想想这件事情(还有心理打击),我认为这可能是正确做到这一点的唯一方法。 – 2008-10-04 02:32:01

1

List<T>使用此:

try 
{ 
    Array.Copy(this._items, 0, array, index, this.Count); 
} 
catch (ArrayTypeMismatchException) 
{ 
    //throw exception... 
} 
0

这里尝试/捕获的与反射一个小测试:

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()); 

在Release模式所得到的输出是:

Try/Catch: 1.7501001 
IsAssignableFrom: 0 

在调试模式下:

Try/Catch: 1.8171039 
IsAssignableFrom: 0.0010001 

结论,只是做反射检查。这很值得。