2012-05-13 13 views
13

Nel mio serialiser/deserialiser, ho il seguente frammento di codice:Il modo più veloce per verificare se un tipo è blittabile?

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

il cui scopo è quello di provare a scrivere una serie di tipi di valore a un ruscello, nel modo più efficiente possibile (cioè, solo il contenuto come un mucchio di byte).

Il problema si presenta quando il tipo è un tipo di valore ma non blittabile e Alloc() non riesce. Al momento viene rilevata l'eccezione e il controllo passa al codice che gestisce l'array come se fosse costituito da tipi di riferimento.

Questo controllo tuttavia (a causa del lancio e della cattura dell'eccezione che a mio avviso è molto lento) si sta rivelando un grave collo di bottiglia a causa del numero di tipi di valore riscontrati nella mia applicazione. Quindi mi chiedo, qual è il modo più veloce per verificare se un tipo è blittabile?

+0

Ho avuto lo stesso problema, ho finito con il caching dei risultati per ogni tipo (ad esempio nel dizionario statico). Il controllo è stato fatto come qui, prova/cattura. –

risposta

4

sto usando classe generica ai risultati della cache. Il test viene eseguito nello stesso modo (tentando di allocare l'handle bloccato).

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

Il caching è quello che ho finito per fare, anche se penso che la tua tecnica di caching sia la più efficiente che abbia mai visto! – sebf

0

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

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

Grazie, ma sfortunatamente questo non funziona. La proprietà IsLayoutSequential è vera per almeno un tipo non bliabile che ho provato (una semplice struttura con una stringa). – sebf

6

La risposta attuale funziona per il caso della interrogante ma, secondo la specifica, array di tipi di valore copiabili sono anche tipi copiabili stessi. Estesa metodo di Ondřej un po ', in modo che ne tiene conto, e funziona anche per i tipi di riferimento:

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

Come effetto collaterale, questo ritorna (seppur correttamente) false per string, perché GetUninitializedObject non può crearla. Supponendo che Alloc verifichi realmente la possibilità di blittare (ad eccezione di string), ciò dovrebbe essere affidabile.

+0

Questo restituirà 'false' con' int [] 'che è tuttavia blittabile. Rimuovi il NOT da '! Elem.IsValueType' per risolvere :) – FooBarTheLittle

+0

@FooBarTheLittle Grazie! – IllidanS4

+0

Prego. – FooBarTheLittle

2

L'eccellente codice di @ IllidanS4 in questa pagina restituisce erroneamente false per gli array in cui l'elemento è blittabile formatted type, il che significa che anche l'array è blittabile. Partendo da questo esempio, mi fisso che problema e aggiunti elevatori in alcuni casi più utilizzata in maniera impropria, come ad esempio:

  • T[] dove T: tipo formattato (appena citato)
  • array frastagliati int[][][]...
  • enums (ma non System.Enum)
  • interfacce, tipi astratti
  • tipi generici (mai blittabili).

Ho anche aggiunto presentate casi per evitare la costosa Exception blocco un po 'più esaustivo e corse test unitari per tutti i diversi tipi di tipo I potuto pensare.

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

Il bel meccanismo di memorizzazione nella cache dell'altra risposta deve essere utilizzato così com'è.

+0

Bella idea per verificare altri tipi. C'è solo un piccolo errore, 'bool' e' char', mentre primitivo, non è blittabile (la dimensione dipende dalla piattaforma). Anche gli array frastagliati non dovrebbero essere modificabili, poiché sono matrici di riferimenti a oggetti. Né sono matrici multidimensionali, per [MSDN] (https://msdn.microsoft.com/en-us/library/75dwhxf7%28v=vs.110%29.aspx), sebbene il mio codice abbia lo stesso problema. – IllidanS4

Problemi correlati