2008-10-04 5 views
4

Хорошо, моя проблема была в этом: я реализовал IList<T>. Когда я добрался до CopyTo(Array array, int index), это было мое решение:Правильная проверка типа NULL в 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. Ключ is всегда возвращает false при проверке переменной, которая установлена ​​в null. Теперь, это удобно для всех видов причин, в том числе избегая пустых разыменовывает и т.д. То, что я наконец-то придумал для моей проверки типа был такой:

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 
    } 
} 

EDIT: Хорошо, забыл укажите что-то. Пара наивно использует то, что в этом коде читается как element.IsAssignableFrom(typeof(T)). Вы должны также позволяют typeof(T).IsAssignableFrom(elementType), как это делает BCL, в случае, если разработчик знает, что все значения в данном конкретном ICollection фактически типа S, полученного из T, и передает массив типа 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

Вот немного проверка TRY/улова против отражения:

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 

Заключение, просто сделать чек отражения. Это стоит того.

Смежные вопросы