When C# is compiled, the generated CIL of a program that uses a generic will contain references to a generic instance. For example:
List<int> x = new List<int>(); x.Add(1); x = 2;
After compiling, we find CIL calls to create the generic List<int>, add 1 to the list, and reset the first element in the list to be 2:
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor() IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0/*int32*/) IL_0012: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::set_Item(int32, !0/*int32*/)
Notice that in each call, a generic instance is referenced,while the signature of the method contains the instantiated generic parameters, not the generic instance arguments substituted. You may ask: “Why aren’t the generic parameters substituted with the actual argument System.Int32?” The reason is probably because the method is encoded as a name/signature encoding, so that it can be found in the referenced assembly (mscorlib). You can see using DotPeek that in the MemberRef table for the set_Item method, there are three fields used to define the method called: (1) the declaring type System.Collections.Generic.List`<System.Int32>, which is an instantiated generic; (2) the name of the method, set_Item; and (3) the signature blob System.Void (System.Int32, !0). In order to find the CIL for the method in mscorlib, a compiler would need to find a method with the same name and same signature. It’s easier to get a match when the generic parameter is used, and not the generic argument.
The problem with these incomplete signatures is that the generic parameter is already typed. Campy fixes this problem by creating new MethodReference values that fully type the method parameters. It performs unification of signatures instead of a simple string comparison for matching. Thus, System.Collections.Generic.List`1<>::set_Item(Int32, !0) matches System.Collections.Generic.List`1<Int32>::set_Item(Int32, Int32). This change required quite a bit of jumping through hoops because the Mono Cecil’s assembly and metadata resolvers could not be used. I had to write new ones. The next release of Campy will add in this new code.