什么是Type.GUID以及它与Type.Equals()的关系?

时间:2011-12-02 17:36:50

标签: c# generics types runtime equals

在尝试将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)) { ... }

这是我的证据:

 screenshot of debug watch window - Equals() returns false on types, true on GUIDs

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

假设:我假设Type GUID唯一标识完全限定类型,包括屏幕截图中factoryInfo.ReturnType丢失的AssemblyQualifiedNamenull值。)

我的假设是错的吗?

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

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

4 个答案:

答案 0 :(得分:22)

对Jared的(完全正确的)答案进行扩展:

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

在.NET世界中,类型相等更复杂。一种类型与特定组件相关联。但不仅如此!如果你加载相同的程序集两次(例如,一次按其程序集名称和一次由其磁盘位置)并要求两个程序集“相同”类型,您将获得两个不同的类型对象,他们虽然很明显它们具有相同的GUID,但它们不会相等。

显然,这是一个重要的出发点; .NET和COM在这方面完全不兼容。互操作必须发生时会发生什么?不知何故,COM和.NET必须就如何在相同的过程中进行比较时如何比较类型的规则达成一致。 (因为.NET正在调用COM代码,反之亦然。)

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

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

这在共同场景中呈现出重大的潜在问题。假设您编写了一个与大型复杂COM库互操作的.NET程序。只是为了选择一个完全非随机的例子,假设你已经为Word编写了一个托管扩展,它有一个绝对巨大的COM“表面区域”。此表面区域通过主互操作程序集暴露给.NET世界,主要互操作程序集包含“虚拟”类型,这些类型与COM世界中的核心响应接口具有所有相同的GUID。然后可以编写.NET代码以通过“虚拟”对象与COM层进行通信,这些对象看起来像适当的接口类型的对象,并且将.NET代码视为适当的.NET类型的对象。 / p>

这样就可以了。然后,您将.NET库发送给客户,并且您意识到 Word不会自动将PIA发送给客户。相反,需要运送他们的PIA。这是巨大的。

因此诞生了C#4的“无PIA”功能。在C#4中,您可以生成一个Word扩展,它只为的内部副本生成它实际使用的单词PIA 。这通常要小得多。然后,您可以重新分发扩展库,而无需重新分发大型PIA。

但这会立即出现问题。假设有两个这样的库,并且他们希望使用PIA 中两者共有的接口类型相互通信?从.NET的角度来看,类型是按装配;从COM的角度来看,每个库中PIA类型的副本相同,但从.NET的角度来看,不同

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

当然,细节要比简单的草图复杂得多。我的观点很简单,就是你在这里开了一大堆蠕虫; GUID如何与类型相等相互作用是一个深层而棘手的主题,很少有人能够完全理解。

答案 1 :(得分:13)

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

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

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

答案 2 :(得分:2)

对于Type比较,您不应该真正依赖System.Type的GUID属性。特别是在进程间通信(WCF,远程处理)中,Guid可能不一样(除非在JaredPar的例子中手动指定)。

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

我不确定为什么你上面的平等比较失败了。它不应该。在下面这个非常简单的例子中,等于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为您创建的动态生成的类型...如果是这种情况,您应该通过单独比较泛型参数来获得所需的结果:

答案 3 :(得分:-5)

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

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

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