2010-01-12 17 views
11

Por varias razones, necesito implementar un mecanismo de caché de tipo en C#. Afortunadamente, CLR proporciona Type.GUID para identificar de forma única un tipo. Lamentablemente, no encuentro ninguna forma de buscar un tipo basado en este GUID. Hay Type.GetTypeFromCLSID() pero según mi comprensión de la documentación (y experimentos) que hace algo muy, muy diferente.Obtiene el tipo de GUID

¿Hay alguna manera de obtener un tipo basado en su GUID menos de bucle a través de todos los tipos cargados y en comparación con sus GUID?

EDIT: Olvidé mencionar que realmente me gustaría una "huella dactilar tipográfica" de ancho fijo, es por eso que el GUID es tan atractivo para mí. En un caso general, por supuesto, el nombre completo del tipo funcionaría.

+0

'typeof() AssemblyQualifiedName' no encaja ¿para ti? –

+0

Rubens: Sí, pero preferiría algo de un ancho fijo, ya que simplificaría mucho mi algoritmo (que está fuera del alcance de esta pregunta) y el tamaño fijo de 16 bytes de un GUID sería ideal. Tal vez podría usar el código hash del nombre calificado pero, una vez más, tendría el mismo problema al buscar el tipo basado en el hash. –

+1

y esto también puede ser útil: http://ayende.com/Blog/archive/2008/01/12/System.Type.GUID-stability.aspx –

Respuesta

3

No bucle para comparar. Llene un Dictionary<Type> y use el método Contains.

Dictionary<Type> types = new Dictionary<Types>(); 
... //populate 

if (types.Contains(someObject.GetType())) 
    //do something 

Esto sin duda le dará una entrada de tamaño fijo, ya que todos ellos habrá referencias a objetos (instancias de Tipo siendo esencialmente objetos de fábrica).

+0

Sí, estaba considerando eso ... Todavía tendría que rellenar el diccionario antes de iniciar el algoritmo y eso agregaría un poco de sobrecarga a la puesta en marcha. No estoy seguro de cuánto es exactamente, así que haré algunas pruebas y veré si es factible hacerlo (aún así es mejor que hacer bucles). –

+0

La búsqueda de hash será ridículamente más eficiente que la comparación iterativa, por lo que si la comparación ocurre en un bucle vale la pena. Pero lo haría de todos modos por la legibilidad del código. –

+0

También podría almacenar una asignación de GUID a nombre calificado en una estructura de datos eficiente (montón o trie). Esta asignación ya podría generarse en tiempo de compilación y almacenarse en un formato binario de análisis rápido. Eso podría reducir la sobrecarga antes de lanzar su algoritmo. Si necesita un objeto 'Type' real, puede crearlo perezosamente a partir del nombre calificado. –

7

¿por qué no utilizar la propiedad designada para eso, es decir. AssemblyQualifiedName? Esta propiedad está documentada como "puede persistir y luego usarse para cargar el Tipo".

El GUID es para interoperabilidad COM.

+0

Gracias, pero como mencioné en uno de los comentarios, idealmente tendría un tipo de "huella digital" de ancho fijo, el manejo de cadenas de longitud variable haría mi vida un poco más complicada. Pero sí, en general, si no tiene ese requisito, entonces estoy de acuerdo, el nombre completo es el camino a seguir. –

+2

@Doc: ¿Qué sucede si crea más de 2^128 tipos? –

+5

Anon: No va a suceder :) El universo colapsará mucho antes de que todas las computadoras del mundo juntas puedan crear 2^128 tipos. –

0

Si usted está en control de estas clases yo recomendaría:

public interface ICachable 
{ 
    Guid ClassId { get; } 
} 

public class Person : ICachable 
{ 
    public Guid ClassId 
    { 
     get { return new Guid("DF9DD4A9-1396-4ddb-98D4-F8F143692C45"); } 
    } 
} 

puede generar su GUID utilizando Visual Studio, Herramientas-> Crear GUID.

+0

Gracias, pero desafortunadamente no tengo el control de los tipos. Debo ser capaz de tratar con cualquier tipo (bueno, excepto por los punteros, pero eso es irrelevante para la pregunta). –

+0

Quizás pueda combinar la idea del diccionario y las guías. Puede poblarlo perezosamente, si el tipo no existe en el diccionario, genere un Guid y añádalo al diccionario. Use una clave de diccionario-> Escriba el nombre, valor-> Guid. De alguna manera tiene que persistir en el diccionario, también supongo que – Serkan

1

¿Qué pasa (de Generating Deterministic GUIDs):

private Guid GetDeterministicGuid(string input) 
{ 
    // use MD5 hash to get a 16-byte hash of the string: 
    MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 
    byte[] inputBytes = Encoding.Default.GetBytes(input); 
    byte[] hashBytes = provider.ComputeHash(inputBytes); 

    // generate a guid from the hash: 
    Guid hashGuid = new Guid(hashBytes); 
    return hashGuid; 
} 

Y tirar en ese typeof().AssemblyQualifiedName. Podría almacenar estos datos dentro de una colección Dictionary<string, Guid> (o, lo que sea, un <Guid, string>).

De esta forma siempre tendrá el mismo GUID para un tipo determinado (advertencia: colisión es posible).

+0

Todavía tendría que hacer una búsqueda de alguna manera ya que este mecanismo hash es intrínsecamente unidireccional. Volvemos al mismo problema. –

+0

Me quedé sin viñetas: buena suerte, doc. –

0

La documentación de Mono informa que un módulo tiene un Metadata heap of guids.

¿Quizás Cecil podría ayudarlo a buscar un tipo basado en su guía? Aunque no estoy seguro, hay una clase GuidHeap, parece que está generando las guías, pero ¿quizás esto sea suficiente para que funcione la caché?

+0

En Mono, Type.GUID siempre devuelve GUID.Empty si la clase no puede especificar manualmente un GUID. (http://lists.ximian.com/pipermail/mono-devel-list/2005-September/014530.html) – EricLaw

4

Esto puede ser solo un resumen de las respuestas ya publicadas, pero no creo que haya una forma de hacerlo sin antes crear un mapa de Guid-> Tipo.

Hacemos esto en nuestro marco de inicialización:

static TypeManager() 
    { 
     AppDomain.CurrentDomain.AssemblyLoad += (s, e) => 
     { 
      _ScanAssembly(e.LoadedAssembly); 
     }; 

     foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      _ScanAssembly(a); 
     } 
    } 

    private static void _ScanAssembly(Assembly a) 
    { 
     foreach (Type t in a.GetTypes()) 
     { 
      //optional check to filter types (by interface or attribute, etc.) 
      //Add type to type map 
     } 
    } 

controlar el evento AssemblyLoad se encarga de ensamblajes de carga dinámica.

Por lo que entiendo, Type.GUID utiliza la versión de ensamblaje del tipo como parte del algoritmo de generación de Guid.Esto puede ocasionar problemas si incrementa los números de versión de su ensamblaje. Usar el método GetDeterministicGuid descrito en otra respuesta probablemente sea aconsejable, dependiendo de su aplicación.

+0

+1, buena respuesta :) –

0

me gustaría utilizar el typeof (clase) .GUID para encontrar la instancia en el diccionario caché:

private Dictionary<Guid, class> cacheDictionary { get; set; } 

y me gustaría tener un método para devolver el diccionario y el GUID como parámetro del método para buscar para la clase en el diccionario.

public T Getclass<T>() 
    { 
     var key = typeof(T).GUID; 
     var foundClass= cacheDictionary.FirstOrDefault(x => x.Key == key); 
     T item; 
     if (foundClass.Equals(default(KeyValuePair<Guid, T>))) 
     { 
      item = new T() 
      cacheDictionary.Add(key, item); 
     } 
     else 
      item = result.Value; 

     return item; 
    } 

y me gustaría utilizar un patrón Singleton para la caché,

y la llamada sería algo así como el código de abajo:.

var cachedObject = Cache.Instance.Getclass<class>(); 
Cuestiones relacionadas