【问题标题】:What is Type.GUID and how does it relate to Type.Equals()?什么是 Type.GUID,它与 Type.Equals() 有什么关系?
【发布时间】:2012-01-11 17:13:45
【问题描述】:

我在尝试将 System.RuntimeType 的实例与泛型类型 TOut 进行比较时遇到了一些有趣的行为:

Type runtimeT = methodInfo.ReturnType; // get RuntimeType using reflection
Type genericT = typeof(TOut);

// This condition fails because runtimeT doesn't 
// seem to include an assembly qualified name
if(runtimeT.Equals(genericT)) { ... }

这是我的证据:

免责声明:我不确切知道在 CLR/类型系统的上下文中 GUID 是什么,当然首字母缩略词代表 全局唯一标识符。也许这个名字误导了我。

假设:我在这里假设 aType GUID 唯一标识完全限定类型,包括屏幕截图中 factoryInfo.ReturnType 中缺少的 AssemblyQualifiedName(null 值.)

我的假设错了吗?

  • 是的:类型 GUID 真正代表什么,它是如何使用的?

  • 否:为什么不通过比较 GUID 来实现Equals()?

【问题讨论】:

    标签: c# generics types runtime equals


    【解决方案1】:

    进一步扩展 Jared 的(完全正确的)答案:

    在 COM 世界中,每个接口都由一个全局唯一标识符标识。在 COM 中没有“改变”接口这样的事情;接口必须永远相同。相反,您创建一个新界面并为其提供一个新的 GUID。任何两个不同的接口都需要具有不同的 GUID。接口相等在 COM 中定义为 GUID 相等。

    在 .NET 世界中,类型相等更加复杂。一方面,类型与特定程序集相关联。但不仅如此!如果您两次加载同一个程序集(例如,一次通过其程序集名称,一次通过其磁盘位置)并要求这两个程序集“相同”类型,您将获得两个 不同 类型的对象,它们即使它们显然具有相同的 GUID,也不会相等。

    显然这是一个主要的出发点; .NET 和 COM 在这方面是非常不兼容的。当互操作必须发生时会发生什么?不知何故,COM 和 .NET 必须在一些规则上达成一致,即当两者都在同一个进程中运行时,如何比较类型是否相等。 (因为 .NET 正在调用 COM 代码,反之亦然。)

    因此,您可以在 .NET 中执行的操作是说“此类型与此 GUID 相关联”。当 COM 规则适用时,COM 代码将通过比较 GUID 来比较两种类型的相等性,因为这就是 COM 世界中相等的含义。

    在 .NET 中,使用 .NET 的常用规则比较类型是否相等。

    这会在常见情况下出现一个重大的潜在问题。假设您编写了一个 .NET 程序,该程序与一个大型复杂的 COM 库进行互操作。仅举一个完全非随机的示例,假设您已经为 Word 编写了一个托管扩展,它具有绝对巨大的 COM“表面积”。此表面区域通过主互操作程序集向 .NET 世界公开,该程序集包含与 COM 世界中对应的接口具有所有相同 GUID 的“虚拟”类型。然后可以编写 .NET 代码以通过“虚拟”对象与 COM 层对话,这些“虚拟”对象在 COM 中看起来像适当接口类型的对象,并且在 .NET 代码中看起来是适当 .NET 类型的对象。

    所以效果很好。然后您将 .NET 库发送给客户,然后您意识到 Word 不会自动将 PIA 发送给客户。相反,您需要运送他们的 PIA。这是巨大的。

    因此诞生了 C# 4 的“无 PIA”特性。在 C# 4 中,您可以生成一个 Word 扩展,该扩展只制作它实际使用的单词 PIA 的部分。通常要小得多。然后,您可以在不重新分发大型 PIA 的情况下重新分发您的扩展库。

    但这立即带来了一个问题。假设有两个这样的库,并且他们想在PIA中使用两者通用的接口类型来相互通信?从 .NET 的角度来看,类型是每个程序集的;从 COM 的角度来看,每个库中 PIA 类型的副本相同,但从 .NET 的角度来看不同。

    因此,我们在 v4 CLR 中添加了一项特殊功能。在这种情况下,两个不同程序集中的两种类型(以及 PIA 类型,如果存在的话!)由 CLR 统一,并且 被 GUID 视为相同,匹配 COM 行为。

    当然,细节比那个简单的草图要复杂得多。我的意思很简单,你在这里打开了一个巨大的蠕虫罐头; GUID 如何与类型相等性交互是一个深奥而棘手的主题,很少有人能完全理解。

    【讨论】:

    • 不错的答案...这是否意味着 .NET 4.0s CLR 缓解了通过反射调用已加载两次的程序集的方法的问题(例如,一次来自字节,一次来自名称)如果参数类型的实例是使用程序集的实例 1 创建的并且 MethodInfo 是使用程序集的实例 2 创建的,那么之前会抛出异常??
    • @JeffN825:不。更改主要集中在 COM 互操作场景上。两次加载的程序集中的类型的行为规则与以往相同。
    • 太糟糕了... :( ...我意识到这是一个很难有效解决的问题,但我认为这是 dll hell 在 .NET 中仍然存在并且运行良好的主要原因。看看人们不遗余力地尝试有效地支持对不同版本程序集的引用(ILMerge、作为嵌入资源加载为字节数组的程序集、沙盒 AppDomains、古怪的 AssemblyResolve 和 TypeResolve 处理程序,必须禁用强名称验证等。
    【解决方案2】:

    Type.GUID 不被用作Equals 的比较的一个原因是它是用户可控制的项目。例如,我可以通过执行以下操作来指定我的interface 的GUID

    [Guid("2bfd006d-94b9-43af-843f-5b32f7567086")]
    interface IFoo { ... }
    

    这是为 COM 互操作生成接口所必需的。如果 Equals 依赖于 GUID 对某个类型是唯一的,那么给定的开发人员可以通过给他们的类型提供与 string、int 等相同的 GUID 来对系统造成严重破坏......

    【讨论】:

      【解决方案3】:

      您不应该真正依赖 System.Type 的 GUID 属性来进行类型比较。特别是在进程间通信(WCF、Remoting)中,Guid 可能不一样(除非它像 JaredPar 的示例中那样手动指定)。

      在内部,Type.Equals 使用 RuntimeTypeHandle 进行相等比较。

      我不确定为什么上面的相等比较失败了。它不应该。在下面这个非常简单的例子中,equal 返回true。

          static void Main(string[] args)
          {
              Test<object>();
          }
      
          static object Test<T>()
          {
              var currentMethod = ((MethodInfo) MethodBase.GetCurrentMethod());
              var oType = currentMethod.ReturnType;
              var genericType = typeof (T);
              var equal = oType.Equals(genericType); // result is true.
              return null;
          }
      

      我在黑暗中随机猜测是您正在使用启用了代理创建的实体框架?在这种情况下,IEntitySet 的泛型参数 T 是 Entity Framework 为您创建的动态生成的类型...如果是这种情况,您应该通过单独比较泛型参数来获得所需的结果:

      【讨论】:

      • 我注意到您的示例没有使用反射来获取oType。可能是因为我从MethodInfo 访问ReturnType,它缺少泛型类型的编译时元数据,导致比较失败?
      • 好主意...但不,我不这么认为,至少在正常使用中是这样。我已经更新了示例以使用反射来获取方法信息...但我应该问一个更好的问题是您如何获得 MethodInfo...因为这肯定会影响您的结果。
      • 你是对的。我正在使用Type.GetMethod() 创建我的MethodInfo,并且由于我所反映的方法具有通用返回类型IObjectSet&lt;T&gt;,因此未设置类型参数T。我不得不使用MethodInfo.MakeGenericMethod() 来设置类型参数,而Type.Equals() 现在为有问题的代码返回true。
      【解决方案4】:

      GUID 是随机生成的字节序列(默认为 16 个),半保证不会重复 - 不会跨计算机或跨时间重复。半保证,因为确实存在重复的可能性,但它是如此微不足道,因此不予考虑。

      因此,大多数程序员在需要提供 ID 时使用 GUID,他们担心该 ID 会与可能最终存在于同一领域中的另一个 ID(正在运行的程序或表的 ID)发生冲突行或一百万其他东西)

      但简而言之,您可以将 GUID 视为代表 ID 的随机数,长度为 16 个字节,并且永远不会生成两次。

      【讨论】:

      • OP 询问的是 Type.GUID 属性,而不是一般的 GUID。
      • 你会发现一个 GUID 是 16 字节(128 位)
      • 您并没有真正尝试回答 OP 提出的实际问题。此外,“默认”不是 16 个字节;它是 16 个字节。时期。 -1
      • 其实是完全相反的。 Type.GUID 值在不同机器上是相同的。必须使它对 COM 互操作有用。它根本不是随机的,它是从接口定义的字符串化版本的 MD5 哈希创建的。
      • 人名不是随机的吗??
      猜你喜欢
      • 2011-03-28
      • 2019-07-22
      • 2012-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多