Pregunta

Ok, mi problema real era este: estaba implementando un IList<T>. Cuando llegué a CopyTo(Array array, int index), esta fue mi solución:

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

Esto funcionó en mi código original, y aún funciona. Pero tiene una pequeña falla, que no se expuso hasta que comencé a construir pruebas para ella, específicamente esta:

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

    coll.CopyTo(testArray, 2);
}

Ahora, esta prueba debería pasar. Lanza el ArgumentException sobre no poder lanzar. ¿Por qué? array[0] == null. La palabra clave is siempre devuelve falso cuando se verifica una variable que se establece en null. Ahora, esto es útil por todo tipo de razones, que incluyen evitar desreferencias nulas, etc. Lo que finalmente se me ocurrió para mi verificación de tipo fue esto:

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

Esto no es exactamente elegante, pero funciona ... ¿Existe una mejor manera?

¿Fue útil?

Solución

La única manera de estar seguro es con reflexión, pero el 90% del tiempo puede evitar el costo de eso usando array is T[]. La mayoría de las personas van a pasar una matriz correctamente tipeada, por lo que será suficiente. Pero, siempre debe proporcionar el código para hacer la verificación de reflexión, por si acaso. Así es como se ve mi placa de caldera general (nota: escribí esto aquí, de memoria, por lo que podría no compilarse, pero debería dar la idea 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, olvidé señalar algo. Un par de respuestas ingenuamente usaron lo que, en este código, se lee solo como element.IsAssignableFrom(typeof(T)). Usted debería también permitir typeof(T).IsAssignableFrom(elementType), como lo hace el BCL, en caso de que un desarrollador sepa que todos los valores en este ICollection específico son en realidad del tipo S derivado de T, y pasa una matriz de tipo S[]

Otros consejos

Hay un método en Tipo específicamente para esto, intente:

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

List<T> usa esto:

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

Aquí hay una pequeña prueba de prueba / captura versus reflexión:

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

El resultado resultante en modo Release es:

Try/Catch: 1.7501001
IsAssignableFrom: 0

En modo de depuración:

Try/Catch: 1.8171039
IsAssignableFrom: 0.0010001

Conclusión, solo haz la comprobación de reflexión. Vale la pena.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top