Pergunta

Ok, o meu problema real era o seguinte: eu estava implementando um IList<T>. Quando cheguei ao CopyTo(Array array, int index), esta foi a minha solução:

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

Isso funcionou no meu código original, e ainda funciona. Mas tem uma pequena falha, que não foi exposto até que eu comecei a construir testes para ele, especificamente esta:

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

    coll.CopyTo(testArray, 2);
}

Agora, este teste deve passar. Ele lança a ArgumentException sobre não ser capaz de lançar. Por quê? array[0] == null. A palavra-chave is sempre retorna false quando verificar uma variável que está definido para null. Agora, isso é útil para todos os tipos de razões, incluindo evitando dereferences nulos, etc. O que eu finalmente veio com para o meu tipo de verificação foi o seguinte:

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

Este não é exatamente elegante, mas funciona ... Existe uma maneira melhor ainda?

Foi útil?

Solução

A única maneira de ter certeza é com reflexão, mas 90% do tempo você pode evitar o custo do que usando array is T[]. A maioria das pessoas estão indo para passar uma matriz corretamente digitado, de modo que vai fazer. Mas, você deve sempre fornecer o código para fazer o check reflexão, assim, apenas no caso. Aqui está o que os meus caldeira-plate geral olha como (nota: Eu escrevi isso aqui, de memória, de modo que isso pode não compilar, mas deve dar a idéia básica):

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

Editar : Ok, esqueceu-se de ponto algo fora. Um casal responde ingenuamente usou o que, neste código, lê-se como só element.IsAssignableFrom(typeof(T)). Você deve também permitem typeof(T).IsAssignableFrom(elementType), como o BCL faz, no caso de um desenvolvedor sabe que todos os valores nesta ICollection específica são realmente de um S tipo derivado de T, e passa uma matriz do tipo S[]

Outras dicas

Existe um método no tipo especificamente para isso, tente:

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

List<T> usa esta:

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

Aqui está um pequeno teste de try / catch vs. reflexão:

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

A saída resultante no modo de versão é:

Try/Catch: 1.7501001
IsAssignableFrom: 0

Em modo de depuração:

Try/Catch: 1.8171039
IsAssignableFrom: 0.0010001

Conclusão, basta fazer a verificação de reflexão. pena do-lo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top