An Assembly Identity is simply an attributes bag. It consists of a set of attributes. Each attribute has a pre-defined range of acceptable value.
There are two kinds of Assembly Identity. One is called DefinitionIdentity. The other one is called ReferenceIdentity.
DefinitionIdentity is an integral part of an assembly. Each assembly has one and only one DefinitionIdentity.
For strongly named assembly, the reverse is supposed to be true, that a DefinitionIdentity will uniquely identify an assembly. In practice, not every people change their assembly version number often. That means multiple different assemblies will have the same DefinitionIdentity.
DefinitionIdentity is tied to a particular assembly. DefinitionIdentity does not exist without an assembly.
In .Net framework 1.0/1.1, a DefinitionIdentity has four build-in attributes: Name, Version, Culture, and PublicKey(Token). In .Net framework 2.0, we introduced a new attribute ProcessorArchitecture. It is likely that we will introduce new attributes in the future.
ReferenceIdentity is an identity that is used to refer to an assembly.
At runtime, fusion applies binding policies on a ReferenceIdentity to get a post-policy ReferenceIdentity (still a reference identity), the probe the world based on a defined rule to find an assembly. Once the assembly is found, fusion extracts the DefinitionIdentity of the assembly, and does a Ref-Def comparison. If the comparison shows the post-policy ReferenceIdentity are not binding equivalent of the Definition Identity, fusion will return FUSION_E_REF_DEF_MISMATCH, which in turn is translated to the famous FileLoadException The located assembly’s manifest definition with name xxx.dll does not match the assembly reference.
ReferenceIdentity is not tied to any assembly. It is possible that multiple ReferenceIdentities can be resolved to the same assembly at runtime.
Today ReferenceIdentity manifests itself in a few forms:
- In assembly metadata, compilers emit ReferenceIdentities for assemblies the assembly references.
- The assembly name in Assembly.Load is a ReferenceIdentity.
- When you serialize a type, the serialized data contains a ReferenceIdentity.
- The assembly names specified in various config file are ReferenceIdentities.
The difference between DefinitionIdentity and ReferenceIdentity are:
- DefinitionIdentity is tied to a particular assembly, while ReferenceIdentity is not. This is discussed above.
- ReferenceIdentity can be partial, while DefinitionIdentity can not. A ReferenceIdentity can contain only a subset of the build-in attributes, while a DefinitionIdentity will contain all the build-in attributes. For attributes not present when the assembly is compiled, their values are implicitly set to “neutral”.
- Binding policies cannot be applied to DefinitionIdentity. It is meaningless to apply policies on DefinitionIdentity.
Since there are two types of assembly name, there are three types of comparison on assembly name. Ref-Ref, Def-Def, and Ref-Def comparison. There is no Def-Ref comparison.
Two ReferenceIdentities are compared as equal, if and only if they have the same set of attributes, and each attribute has the same value. For example, ReferenceIdentities “name” and “name, culture=neutral” are compared as not equal, since the first one only have one attribute, while the second one has two attributes.
Two DefinitionIdentities are considered as equal, if and only if all the attributes have the same value. By design two DefinitionIdentities will always have the same set of attributes. The comparison is purely on the value of each attribute. For example, DefinitionIdentity “name” and “name, culture=neutral” are compared as equal, since unspecified attributes will carry a default value of “neutral”. While “name, culture=neutral” and “name, culture=en-us” are not equal.
ReferenceIdentity and DefinitionIdentity cannot be compared directly, since they have different semantics. Rather, we say if a given ReferenceIdentity matches a DefinitionIdentity. A ReferenceIdentity matches a DefinitionIdentity, if and only if the value of all the attributes specified in the ReferenceIdentity match the value of the corresponding attributes of the DefinitionIdentity. If an attribute is missing in the ReferenceIdentity, it matches any value for that attribute in DefinitionIdentity. For example, Ref “name” matches Def “name, culture=neutral”, and Def “name, culture=en-us”. But Ref “name, culture=neutral” does not match Def “name, culture=en-us”.
In CLR we have another special comparison — binding comparison for Ref-Def matching. In the special binding comparison context, the version number is ignored when the ReferenceIdentity does not contain any public key (token).
A ReferenceIdentity cannot be transformed to a DefinitionIdentity.
A DefinitionIdentity can be transformed to a ReferenceIdentity. For example, when you serialize a CLR type, the DefinitionIdentity is transformed to a ReferenceIdentity. Later when the type is deserialized, the ReferenceIdentity is used to locate the right assembly.
The transformation from DefinitionIdentity to ReferenceIdentity can (or should) be customized. You can (should) be able to only include a subset of attributes in the transformed ReferenceIdentity. You should also be able to include the full set of attributes in the transformed ReferenceIdentity.
Much of this information is *not* present in CLR today. Though you do see the concepts of references and definitions, there is only one AssemblyName class in CLR. Given an AssemblyName object, you cannot tell whether it is a ReferenceIdentity or DefinitionIdentity. You have to look at it in context to tell which one it is.
This omission does cause confusion on how to compare assembly identities, and how to transform a DefinitionIdentity to a ReferenceIdentity. Understanding the difference between ReferenceIdentity and DefinitionIdentity is big paradigm shift. Unfortunately it is not possible to make such a big move in .Net framework 2.0 at this point. We will look for making the change in the next version of .Net framework.