Skip to content

VB.NET anonymous types decompile to invalid C# (VB$AnonymousType_* use sites, hidden definitions) #3952

Description

@siegfriedpammer

Decompiling VB.NET-compiled assemblies that use anonymous types produces uncompilable C#: anonymous type definitions are hidden from the output, but use sites still reference them by their metadata name.

Reproduction

  • NuGet package QuartzNetWebConsole 1.0.2, assembly QuartzNetWebConsole.Views.dll (VB.NET-compiled), current master, default DecompilerSettings.
  • Decompile e.g. QuartzNetWebConsole.Views.Views (or any member using VB anonymous types / LINQ Select ... Into).

Observed

Use sites emit the raw generated names, which are not valid C#:

select new VB$AnonymousType_2<TriggerWithState, ITrigger>(tr, tr.Trigger) into $VB$It
select new VB$AnonymousType_3<VB$AnonymousType_2<TriggerWithState, ITrigger>, bool>($VB$It, ...) into $VB$It

At the same time DecompileTypeAsString(new FullTypeName("VB$AnonymousType_02"))` yields an empty string, i.e. the definition is suppressed, so the emitted code has no declaration to fall back on.

Analysis

Two generated-name predicates disagree about VB names:

  • SRMExtensions.IsGeneratedName treats names containing $ as generated, and SRMExtensions.IsAnonymousType therefore matches VB$AnonymousType_*. This is what CSharpDecompiler.MemberIsHidden uses (CSharpDecompiler.cs:389), so the definitions are hidden when settings.AnonymousTypes is on.
  • NRExtensions.HasGeneratedName(IType) only matches names starting with/containing <, so the type-system-level NRExtensions.IsAnonymousType(IType) returns false for VB$AnonymousType_*. As a result none of the anonymous-type translation paths in CallBuilder/ExpressionBuilder (e.g. CallBuilder.cs:1412/1838) fire for VB assemblies, and no new { ... } is synthesized.

Aligning NRExtensions.IsAnonymousType/HasGeneratedName with the SRM-level predicate should make VB anonymous types round-trip like the C# ones (<>f__AnonymousType*).

Related, likely a separate work item: VB also emits VB$AnonymousDelegate_* types (visible in the first snippet's generic arguments). C# has no equivalent construct, so those would additionally need mapping to Func<...>/Action<...> or a synthesized named delegate to make the output compilable.

This issue was written by an AI agent (Claude) on Siegfried's behalf.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugDecompilerThe decompiler engine itself

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions