【问题标题】:Get type from GUID从 GUID 获取类型
【发布时间】:2011-01-03 23:52:18
【问题描述】:

由于各种原因,我需要在 C# 中实现一个类型缓存机制。幸运的是,CLR 提供了Type.GUID 来唯一标识一个类型。不幸的是,我找不到任何方法来查找基于此 GUID 的类型。 Type.GetTypeFromCLSID() 但根据我对文档(和实验)的理解,它做了非常非常不同的事情。

是否有任何方法可以根据其 GUID 获取类型,而不是遍历所有加载的类型并与它们的 GUID 进行比较?

编辑:我忘了说我真的想要一个固定宽度的“类型指纹”,这就是为什么 GUID 对我如此有吸引力。当然,在一般情况下,类型的完全限定名称会起作用。

【问题讨论】:

  • typeof().AssemblyQualifiedName 不适合您?
  • Rubens:会的,但我更喜欢固定宽度的东西,因为它会大大简化我的算法(这超出了这个问题的范围)和 16 字节的固定大小GUID 将是理想的。也许我可以使用限定名称的散列码,但是在基于散列查找类型时我会遇到同样的问题。
  • 请记住,散列是一种单向过程。
  • 缓存还是不缓存:这是个问题;无论是承受令人发指的获取时间的吊索和箭矢,还是建立一个缓存并在作曲中损失一些,这都是更高尚的。

标签: c# .net reflection types


【解决方案1】:

为什么不为此使用指定的属性,即。 AssemblyQualifiedName?这个属性被记录为“可以被持久化并在以后用于加载类型”。

GUID 用于 COM 互操作。

【讨论】:

  • 谢谢,但正如我在其中一个 cmets 中提到的,理想情况下我会输入固定宽度的“指纹”,处理可变长度的字符串会使我的生活变得更加复杂。但是,是的,一般来说,如果您没有这样的要求,那么我同意,完整的限定名称就是要走的路。
  • @Doc:如果你创建了超过 2^128 个类型怎么办?
  • 匿名:不会发生 :) 宇宙将在世界上所有计算机一起创造 2^128 种类型之前就崩溃了。
  • 可变长度字段对于 SQLite 等进程中的关系引擎来说非常迅速。
  • Remus Rusanu:我真的不想深入细节,因为它们确实超出了范围,但我有很好的理由使用我所做的数据结构,而且我通常知道我的我在做(至少我愿意这么认为)。开发时间是这里的次要问题。我很想谈论它,因为它真的很有趣,但不幸的是,我不认为这个问题是合适的媒介。不过感谢您的建议,一般来说,您是对的,现成的东西在大多数情况下是最有用的。
【解决方案2】:

不要循环比较。填充Dictionary<Type> 并使用Contains 方法。

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

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

这肯定会给你一个固定大小的条目,因为它们都是对象引用(Type 的实例本质上是工厂对象)。

【讨论】:

  • 是的,我正在考虑...我仍然需要在启动算法之前预先填充字典,这会给启动增加一些开销。我不确定到底有多少,所以我会做一些测试,看看是否可行(尽管它仍然比循环更好)。
  • 哈希查找将比迭代比较高效得多,因此如果比较发生在循环中,这是非常值得的。但为了代码的易读性,我还是会这样做。
  • 您还可以将 GUID 到限定名称的映射存储在有效的数据结构(堆或树)中。这种映射可能已经在编译时生成并以快速解析的二进制格式存储。这可以减少启动算法之前的开销。如果您需要一个实际的 Type 对象,您可以从限定名称懒惰地创建它。
  • divo:不幸的是,编译时生成是毫无问题的,因为我将不得不处理编译时未知的类型。
  • 类命名空间的散列?有趣的。一定是可能的,反射使用它。
【解决方案3】:

如果你能控制这些课程,我会推荐:

public interface ICachable
{
    Guid ClassId { get; }
}

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

您可以使用 Visual Studio、Tools->Create Guid 生成您的 GUID。

【讨论】:

  • 谢谢,但不幸的是我无法控制这些类型。我必须能够处理任何类型(好吧,除了指针,但这与问题无关)。
  • 也许你可以结合字典的想法和指导。您可以懒惰地填充它,如果字典中不存在类型,则生成一个 Guid 并将其添加到字典中。使用字典键-> 类型名称、值-> Guid。我想你也需要一些持久化字典的方式
【解决方案4】:

怎么样(来自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;
}

然后输入typeof().AssemblyQualifiedName。您可以将此数据存储在 Dictionary&lt;string, Guid&gt; 集合(或者,无论如何,&lt;Guid, string&gt;)中。

这样,对于给定类型,您将始终拥有相同的GUID(警告:可能发生冲突)。

【讨论】:

  • 我仍然需要以某种方式进行查找,因为这种哈希机制本质上是单向的。我们又回到了同样的问题。
【解决方案5】:

Mono 文档报告说一个模块有一个Metadata heap of guids

也许Cecil 可以帮助您根据其 guid 查找类型?不过不确定,有一个 GuidHeap 类,虽然它似乎正在生成 guid,但也许这足以让您的缓存工作?

【讨论】:

【解决方案6】:

这可能只是已经发布的答案的摘要,但我认为如果不先构建 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 方法,具体取决于您的应用程序。

【讨论】:

  • “我认为没有先构建 Guid->Type 的地图就没有办法做到这一点” 正确,因为每个任何Assembly 中的Type 仅在实际引用时才按需完全加载。每当第一次加载程序集时,CLR 总是加载程序集中的每个Type 是低效的,因为它的许多类型可能永远不需要或使用。因此,对于尚未引用的Type,CLR 不知道(或查找方法)GuidAttribute 中的 Guid 值。构建 Guid-to-Type 映射至关重要地确保这些类型也全部加载。
【解决方案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>();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-18
    相关资源
    最近更新 更多