Получить тип из GUID

По различным причинам мне нужно реализовать механизм кэширования типов в С#. К счастью, CLR предоставляет Type.GUID уникальную идентификацию типа. К сожалению, я не могу найти способ поиска типа, основанного на этом GUID. Там Type.GetTypeFromCLSID(), но на основе моего понимания документации (и экспериментов), которая делает что-то очень, очень различное.

Есть ли способ получить тип, основанный на его GUID, который не зацикливается на всех загруженных типах и сравнивается с их идентификаторами GUID?

EDIT. Я забыл упомянуть, что мне действительно нравится "отпечаток пальца типа" фиксированной ширины, поэтому GUID так привлекателен для меня. В общем случае, конечно, будет работать полное имя типа.

Ответ 1

Не пытайтесь сравнивать. Заполните Dictionary<Type> и используйте метод Contains.

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

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

Это, безусловно, даст вам запись фиксированного размера, так как все они будут ссылками на объекты (экземпляры типа, по существу являющиеся объектами factory).

Ответ 2

почему бы не использовать для этого назначенное свойство, т.е. AssemblyQualifiedName? Это свойство задокументировано как "может сохраняться, а затем использоваться для загрузки типа".

GUID для COM-взаимодействия.

Ответ 3

Это может быть просто сводка ответов, уже опубликованных, но я не думаю, что есть способ сделать это без предварительного построения карты Guid- > Type.

Мы делаем это в наших рамках при инициализации:

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

Обработка события AssemblyLoad заботится о динамически загружаемых сборках.

Из того, что я понимаю, Type.GUID использует версию сборки типа как часть алгоритма генерации Guid. Это может привести к возникновению проблем, если вы увеличиваете число версий сборки. Использование метода GetDeterministicGuid, описанного в другом ответе, вероятно, будет целесообразным, в зависимости от вашего приложения.

Ответ 4

Как насчет (от Создание детерминированных GUID):

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

И забросьте это typeof().AssemblyQualifiedName. Вы можете сохранить эти данные внутри коллекции Dictionary<string, Guid> (или, что угодно, a <Guid, string>).

Таким образом, у вас всегда будет один и тот же GUID для данного типа (предупреждение: возможен столкновение).

Ответ 5

Если вы контролируете эти классы, я бы рекомендовал:

public interface ICachable
{
    Guid ClassId { get; }
}

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

Вы можете сгенерировать GUID с помощью Visual Studio, Tools- > Create Guid.

Ответ 6

В документации по Mono сообщается, что модуль имеет кучу метаданных для подсказок.

Возможно, Cecil может помочь вам найти тип на основе его guid? Однако не уверен, что существует класс GuidHeap, но, похоже, он генерирует подсказки, но, возможно, этого достаточно для вашего кеша работать?

Ответ 7

Я бы использовал typeof (class).GUID, чтобы найти экземпляр в словаре кеша:

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

и у меня был бы способ вернуть словарь и GUID в качестве параметра метода для поиска класса в словаре.

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

и я бы использовал одноэлементный шаблон для кеша,

и вызов будет выглядеть как код ниже:

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