dnSpyEx / dnSpy

Unofficial revival of the well known .NET debugger and assembly editor, dnSpy
GNU General Public License v3.0
7.02k stars 462 forks source link

cannot compile interface with generic type parameter #277

Open woahbust opened 11 months ago

woahbust commented 11 months ago

dnSpyEx version

6.4.1

Describe the Bug

When trying to compile an interface with a generic type parameter, an error is thrown that the edited interface could not be found.

How To Reproduce

Create an interface, then give it a generic type parameter either by editing the code or through the 'Generic Params' tab from the 'Edit Type' menu. Trying to compile the interface should now throw an error.

Expected Behavior

The interface gets compiled and has a generic type parameter.

Actual Behavior

Compiling throws an error

Additional Context

bug

ElektroKill commented 11 months ago

Hello,

This issue occurs due to a type naming behavior of the C# compiler. When you compile interface interface ITest<T> to IL, we get an interface type with a type name of ITest`1 and with one generic parameter defined. The number after the backtick stores information about the number of generic parameters in a type. This is necessary to allow multiple C# types with the same name to have different generic parameter counts. On the metadata (IL) level, type resolution is performed via the name so having two types with the same name would create a conflict. To circumvent this conflict this backtick and the generic parameter count is added. This interesting naming behavior is also responsible for this issue!

When we create the interface through dnSpy, call it ITest, and add a generic parameter, we get, on the metadata level, an interface type with the name ITest and one generic parameter defined. Notice this differs from what the C# compiler produces! Then when we go to edit the type and click compile, the C# compiler will produce a new interface type with the name ITest`1 and one generic parameter defined. When dnSpy tries to map this new type to the old type to merge the changes, it fails as no type is defined with such a name!

The simple fix is to ensure that the type names match the naming convention used by the C# compiler for types with generic parameters and add the backticks and generic parameter numbers when changing the number of generic parameters.

Something to avoid such mishaps in the future could be added too, perhaps a message box when editing a type that gives the option to automatically update the type name?

Hope this explains the issue well and provides a solution to your problem!

ZzZombo commented 6 months ago

The simple fix is to ensure that the type names match the naming convention used by the C# compiler for types with generic parameters and add the backticks and generic parameter numbers when changing the number of generic parameters.

Sorry, as I struggle with this right now, please elaborate on what is needed to be done in order to compile changed code. EDIT: I have a class named CustomBlockValue with a single generic parameter T. What do you mean by ensuring type names match?

ZzZombo commented 6 months ago

@ElektroKill, could you please lend me a hand with this?

ElektroKill commented 6 months ago

In order for the compile functionality to work your newly added type must have a type name that matches the amount of generic parameters present. If your type (in this case a class) CustomBlockValue has one generic parameter, you must ensure that when you right-click and select Edit Type the name that is shown is CustomBlockValue`1. This is necessary as dnSpy/Roslyn needs this suffix indicating generic parameter count to be present.

Notice the difference in behavior in the following demonstration when the name contains or does not contain the generic suffix. dnSpy_rg5YR4e8tp

ElektroKill commented 6 months ago

Note: If you created a new class in dnSpy and then try to use it when editing a completely different type in C# editor it most likely will not work due to a limitation in the current implementation of the C# editor.