Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is Type.GUID and how does it relate to Type.Equals()?

I came across some interesting behavior while trying to compare an instance of System.RuntimeType with a generic type 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)) { ... }

Here is my evidence:

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

Disclaimer: I don't know precisely what a GUID is in the context of the CLR / type-system, except of course that the acronym stands for global unique identifier. Perhaps the name is misleading me.

Assumption: I'm assuming here that aType GUID uniquely identifies the fully qualified type, including theAssemblyQualifiedName that's missing from factoryInfo.ReturnType in the screenshot (the null value.)

Is my assumption wrong?

  • Yes: What does type GUID truly represent, and how is it used?

  • No: Why wouldn't Equals() be implemented by comparing GUID?

like image 642
Rob Avatar asked Dec 02 '11 17:12

Rob


People also ask

What is GUID type in C#?

We use GUID for unique identifier. Now a question arise if we already have primary key than why we use Globally Unique Identifier(GUID). we use GUID because it have very low probability of being duplicated as it is 128-bit integer(16 bytes) which allow to use GUID across all databse and computer without data collision.

Is GUID value type?

Guid is a Value Type. See MSDN. Note that Guid is a struct. All structs are Value Types.

What is a GUID string?

GUID is a 16 byte binary SQL Server data type that is globally unique across tables, databases, and servers. The term GUID stands for Globally Unique Identifier and it is used interchangeably with UNIQUEIDENTIFIER.

How are GUIDs created?

A GUID (globally unique identifier) is a 128-bit text string that represents an identification (ID). Organizations generate GUIDs when a unique reference number is needed to identify information on a computer or network.


1 Answers

To expand a bit upon Jared's (entirely correct) answer:

In the COM world every interface is identified by a globally unique identifier. There is no such thing as "changing" an interface in COM; interfaces are required to be the same forever. Instead, you create a new interface and give it a new GUID. Any two interfaces that differ are required to have different GUIDs. Interface equality is defined as GUID equality in COM.

In the .NET world, type equality is more complicated. A type is associated with a particular assembly, for one thing. But not just that! If you load the same assembly twice (say, once by its assembly name and once by its disk location) and ask the two assemblies for "the same" type, you will get two different type objects and they will not compare as equal even though obviously they have the same GUID.

Clearly this is a major point of departure; .NET and COM are deeply incompatible in this regard. What happens when interop must occur? Somehow COM and .NET have got to agree on some rules for how types are compared for equality when both are in play in the same process. (Because .NET is calling COM code, or vice versa.)

Thus what you can do in .NET is say "this type is associated with this GUID". When COM rules apply, the COM code will compare two types for equality by comparing GUIDs, because that's what equality means in the COM world.

In .NET, types are compared for equality using the usual rules for .NET.

This then presents a significant potential problem in a common scenario. Suppose you have written a .NET program which interoperates with a large, complex COM library. Just to pick a completely non-random example, suppose you've written a managed extension to Word, which has an absolutely enormous COM "surface area". This surface area is exposed to the .NET world via a Primary Interop Assembly, which contains "dummy" types that have all the same GUIDs as the corersponding interfaces in the COM world. The .NET code can then be written to talk to the COM layer via the "dummy" objects, which look to COM like objects of the appropriate interface type, and look to .NET code to be objects of the appropriate .NET type.

So that works just fine. And then you go to ship your .NET library to customers and you realize Word does not ship the PIA to customers automatically. Rather, you are required to ship their PIA. Which is enormous.

Thus was born the "no PIA" feature of C# 4. In C# 4 you can generate a Word extension that makes an internal copy of only the portions of the word PIA that it actually uses. Which is typically much smaller. You can then redistribute your extension library without redistributing the large PIA.

But this immediately presents a problem. Suppose there are two such libraries, and they want to communicate with each other using the interface types that are common to both, in the PIA? From the .NET perspective, types are per-assembly; the copies of the PIA types in each library are the same from COM's point of view, but different from .NET's point of view.

Therefore we added a special feature in the v4 CLR. In that situation, the two types in the two different assemblies (and the PIA type, if it is present!) are unified by the CLR and are treated as equal by GUID, matching the COM behaviour.

The details of course are much more complex than that simple sketch. My point is simply that you are opening an enormous can of worms here; how GUIDs interact with type equality is a deep and tricky subject that few people understand fully.

like image 103
Eric Lippert Avatar answered Sep 25 '22 23:09

Eric Lippert