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:
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?
A GUID is randomly generated sequence of bytes (16 by default) that is semi-guaranteed to never be repeated - not across computers or time. Semi-guaranteed because the possibility to repeat does exist but it's so minuscule it is not considered.
Because of this, most programmers use GUIDs when they need to provide an ID to something where they worry about the ID having a collision against another ID that may end up existing in the same realm (IDs of programs running, or of table rows or a million other things)
But in short, you can think of a GUID as a random-ish number that represents an ID, is 16 bytes long and won't ever be generated twice.
You shouldn't really rely on System.Type's GUID property for Type comparison. Especially in inter-process communication (WCF, Remoting) , the Guid may not be the same (unless it's manually specified as in JaredPar's example).
Internally, Type.Equals uses a RuntimeTypeHandle for equality comparison.
I'm not sure why your equality comparison above is failing. It shouldn't. In the very simple example below, equal returns
true
.My random shot in the dark guess is that you're using Entity Framework with Proxy Creation Enabled? In which case, generic parameter T of IEntitySet is a dynamically generated type that Entity Framework has created for you... If this is the case, you should get the desired result by comparing the generic arguments individually:
One reason that
Type.GUID
isn't used as the comparison forEquals
is that it's user controllable item. For example i can dictate theGUID
of myinterface
by doing the followingThis is necessary in order to produce interfaces for COM interop. If
Equals
relied on theGUID
being unique to a type then a given developer could wreak havoc on the system by giving their type the sameGUID
as saystring
,int
, etc ...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.