2012-05-13 25 views
13

En mi serializador/deserialiser, tengo el siguiente fragmento:¿La forma más rápida de verificar si un tipo es blittable?

if (element_type.IsValueType && collection_type.IsArray) 
    { 
     try 
     { 
      GCHandle h = GCHandle.Alloc(array_object, GCHandleType.Pinned); 
      int arrayDataSize = Marshal.SizeOf(element_type) * c.Count; 
      var array_data = new byte[arrayDataSize]; 
      Marshal.Copy(h.AddrOfPinnedObject(), array_data, 0, arrayDataSize); 
      h.Free(); 
      WriteByteArray(array_data); 

      return; 
     } 
     catch (ArgumentException) 
     { 
      //if the value type is not blittable, then we need to serialise each array item one at a time 
     } 
    } 

El propósito de los cuales es tratar de escribir una gran variedad de tipos de valor a una corriente, de la manera más eficiente posible (es decir, justo el contenido como un grupo de bytes).

El problema aparece cuando el tipo es de un tipo de valor pero no es blittable, y Alloc() falla. En este momento, se captura la excepción y se pasa el control al código que trata con la matriz como si constara de tipos de referencia.

Sin embargo, este chequeo (debido a que arroja y atrapa la excepción que entiendo es muy lento) está demostrando ser un cuello de botella severo debido a la cantidad de tipos de valores que se encuentran en mi aplicación. Entonces me pregunto, , ¿cuál es la forma más rápida de verificar si un tipo es blittable?

+0

Tuve el mismo problema, terminé con los resultados del almacenamiento en caché para cada tipo (por ejemplo, en el diccionario estático). La comprobación se hizo igual que aquí, intente/atrape. –

Respuesta

4

estoy usando clase genérica a los resultados de caché. La prueba se realiza de la misma manera (tratando de asignar el asa anclada).

public static class BlittableHelper<T> 
{ 
    public static readonly bool IsBlittable; 

    static BlittableHelper() 
    { 
     try 
     { 
      // Class test 
      if (default(T) != null) 
      { 
       // Non-blittable types cannot allocate pinned handle 
       GCHandle.Alloc(default(T), GCHandleType.Pinned).Free(); 
       IsBlittable = true; 
      } 
     } 
     catch { } 
    } 
} 
+0

El almacenamiento en caché es lo que terminé haciendo, aunque creo que su técnica de almacenamiento en caché aquí es la más eficiente que he visto. – sebf

0

Uso http://msdn.microsoft.com/en-us/library/system.type.islayoutsequential.aspx y http://msdn.microsoft.com/en-us/library/system.type.isexplicitlayout.aspx:

element_type.IsValueType && collection_type.IsArray && (element_type.IsLayoutSequential || element_type.IsExplicitLayout) 
+2

Gracias, pero lamentablemente esto no funciona. La propiedad IsLayoutSequential es verdadera para al menos un tipo no blittable que probé (una estructura simple con una cadena). – sebf

6

La respuesta actual funciona para el caso de la pregunta, pero, de acuerdo con la especificación, las matrices de los tipos de valor blittable son también ellos mismos tipos blittable. Extendido método de Ondřej un poco, por lo que tiene esto en cuenta, y también trabaja para los tipos de referencia:

public static bool IsBlittable<T>() 
{ 
    return IsBlittableCache<T>.Value; 
} 

public static bool IsBlittable(Type type) 
{ 
    if(type.IsArray) 
    { 
     var elem = type.GetElementType(); 
     return elem.IsValueType && IsBlittable(elem); 
    } 
    try{ 
     object instance = FormatterServices.GetUninitializedObject(type); 
     GCHandle.Alloc(instance, GCHandleType.Pinned).Free(); 
     return true; 
    }catch{ 
     return false; 
    } 
} 

private static class IsBlittableCache<T> 
{ 
    public static readonly bool Value = IsBlittable(typeof(T)); 
} 

Como efecto secundario, esto devuelve (aunque mal) false para string, porque GetUninitializedObject no puede crearlo. Asumiendo que Alloc realmente verifica la posibilidad de que se rompa (excepto por string), esto debería ser confiable.

+0

Esto devolverá 'false' con' int [] ', que sin embargo es blittable. Elimine NOT de '! Elem.IsValueType' para reparar :) – FooBarTheLittle

+0

@FooBarTheLittle ¡Gracias! – IllidanS4

+0

De nada. – FooBarTheLittle

2

El excelente código de @ IllidanS4 en esta página devuelve incorrectamente false para matrices en las que el elemento es blittable formatted type, lo que significa que la matriz también es blittable. A partir de ese ejemplo, Me fijo que problema y ha añadido manipulación para unos pocos casos más mal manejo, tales como:

  • T[] donde T: de tipo formateado (acabamos de mencionar)
  • matrices dentados int[][][]...
  • enumeraciones (pero no System.Enum sí mismo)
  • interfaces, tipos abstractos
  • tipos genéricos (nunca blittable).

También agregué las fundas para evitar el caro bloque Exception un poco más exhaustivo y realicé pruebas unitarias para todos los tipos de tipos que pude pensar.

public static bool IsBlittable(this Type T) 
{ 
    while (T.IsArray) 
     T = T.GetElementType(); 

    bool b; 
    if (!((b = T.IsPrimitive || T.IsEnum) || T.IsAbstract || T.IsAutoLayout || T.IsGenericType)) 
     try 
     { 
      GCHandle.Alloc(FormatterServices.GetUninitializedObject(T), GCHandleType.Pinned).Free(); 
      b = true; 
     } 
     catch { } 
    return b; 
} 

El buen mecanismo de caché de la otra respuesta debe utilizarse tal cual.

+0

Buena idea para verificar otros tipos. Solo hay un pequeño error, 'bool' y' char', aunque son primitivos, no son blittables (el tamaño depende de la plataforma). Las matrices irregulares tampoco deberían ser blittables, ya que son matrices de referencias a objetos. Tampoco lo son las matrices multidimensionales, por [MSDN] (https://msdn.microsoft.com/en-us/library/75dwhxf7%28v=vs.110%29.aspx), aunque mi código tiene el mismo problema. – IllidanS4

Cuestiones relacionadas