Get type from GUID

2019-04-19 23:24发布

问题:

For various reasons, I need to implement a type caching mechanism in C#. Fortunately, the CLR provides Type.GUID to uniquely identify a type. Unfortunately, I can't find any way to look up a type based on this GUID. There's Type.GetTypeFromCLSID() but based on my understanding of the documentation (and experiments) that does something very, very different.

Is there any way to get a type based on its GUID short of looping through all the loaded types and comparing to their GUIDs?

EDIT: I forgot to mention that I would really like a "type fingerprint" of fixed width, that's why the GUID is so appealing to me. In a general case, of course, the fully qualified name of the type would work.

回答1:

Don't loop to compare. Populate a Dictionary<Type> and use the Contains method.

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

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

This will certainly give you a fixed size entry, since all of them will be object references (instances of Type essentially being factory objects).



回答2:

why not use the designated property for that, ie. AssemblyQualifiedName? This property is documented as "can be persisted and later used to load the Type".

The GUID is for COM interop.



回答3:

This may just be a summary of answers already posted, but I don't think there is a way to do this without first building a map of Guid->Type.

We do this in our framework on initialization:

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

Handling the AssemblyLoad event takes care of dynamically loaded assemblies.

From what I understand, Type.GUID uses the assembly version of the type as part of the Guid generation algorithm. This may lead to trouble if you increment your assembly version numbers. Using the GetDeterministicGuid method described in another answer would probably be advisable, depending on your application.



回答4:

What about (from 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;
}

And throw in that typeof().AssemblyQualifiedName. You could to store this data inside a Dictionary<string, Guid> collection (or, whatever, a <Guid, string>).

This way you'll have always a same GUID for a given type (warning: collision is possible).



回答5:

If you are in control of these classes I would recommend:

public interface ICachable
{
    Guid ClassId { get; }
}

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

You can generate your GUIDs using Visual Studio, Tools->Create Guid.



回答6:

The Mono documentation reports that a module has a Metadata heap of guids.

Perhaps Cecil might help you lookup a type based on its guid? Not sure though, there is a GuidHeap class, it seems to be generating the guids though, but perhaps this is enough for your cache to work?



回答7:

I would use the typeof (class).GUID to find the instance in the cache dictionary:

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

and I would have a method to return the dictionary and the GUID as parameter of the method to search for the class in the dictionary.

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

and I would use a singleton pattern for the cache,

and the call would be something like the code below:

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