Virtual Method Renaming Enterprise

You frequently override system virtual methods like ToString, Equals, and GetHashCode. Demeanor renames the overrides. The runtime continues to dispatch correctly to the renamed methods.

Usage

CLIMSBuildDefault
(enabled by default at Enterprise)(enabled by default at Enterprise)On
--no-virtual-rename<DemeanorNoVirtualRename>true</DemeanorNoVirtualRename>Disable

Before & After

YOUR CODE
public class User
{
    public string Name { get; }
    public string Email { get; }

    public override string ToString()
        => $"{Name} <{Email}>";

    public override bool Equals(object? obj)
        => obj is User other &&
           string.Equals(Email, other.Email, ...);

    public override int GetHashCode()
        => StringComparer.OrdinalIgnoreCase
              .GetHashCode(Email);
}
AFTER OBFUSCATION
public class j
{
    private string m_a;
    private string b;

    public virtual string u()
    {
        return a() + l.a("\u0091\u001c") + a()
               + l.a("<");
    }

    public virtual bool v(object a)
    {
        return a is j j2 &&
            string.Equals(this.a(), j2.a(), ...);
    }

    public virtual int w()
    {
        return StringComparer.OrdinalIgnoreCase
            .GetHashCode(a());
    }
}

Real ILSpy output. ToStringu(), Equalsv(), GetHashCodew(). The runtime continues to dispatch correctly to the renamed methods. Attempting to recompile produces 11+ C# compiler errors.

What It Covers

Virtual override renaming handles every common shape of override and interface implementation, including:

  • Overrides of standard .NET methodsToString, Equals, GetHashCode, and other base-method overrides.
  • Overrides of framework virtuals — lifecycle and event-handler methods you override from a framework base class.
  • Interface implementations from external libraries — standard runtime interfaces and custom interfaces from third-party packages.
  • Public interfaces with internal implementations — the public contract stays intact; the internal implementation is opaque. Useful when you ship a library API to consumers but obfuscate the internal classes that satisfy it.
  • Hierarchies preserved by [Obfuscation] — if the base of an override chain must keep its name (for reflection or external binding), Demeanor preserves the contract while renaming derived overrides.
  • Diamond shapes — a single method satisfying both an interface slot and an abstract base slot.
  • Generic interfaces — e.g. IProcessor<T> with a closed implementation Impl : IProcessor<int>.
  • Reflection-anchored methods — when typeof(SpecificClass).GetMethod("Foo") appears in your code, only that specific method is preserved; other methods named Foo on unrelated classes still rename.

Framework Notes

Overrides of framework base classes and implementations of framework interfaces in the following frameworks rename automatically — you do not need annotations on the override itself. DTOs your code serializes through these frameworks are a separate concern (serializers bind to property names) and may still need attribute annotations.

  • WinForms — control lifecycle and event-handler overrides rename automatically.
  • WPF — element lifecycle and notification overrides rename automatically. Dependency-property change callbacks are delegates and rename freely.
  • ASP.NET Core — controller actions, custom middleware, action filters, and authorization handlers all rename automatically.
  • JSON converters (System.Text.Json or Newtonsoft) — the converter overrides themselves rename. The DTOs you serialize are a separate concern: annotate them with [Obfuscation(Exclude=true, ApplyToMembers=true)] or, for System.Text.Json, register them with a source-generated JsonSerializerContext (which Demeanor auto-detects).
  • MessagePack — formatter implementations rename. DTOs using numeric keys obfuscate fully; DTOs using named keys need annotations on the DTO.
  • XmlSerializer — serialization-callback implementations rename. Annotate the serialized DTO types.

Obfuscation Report Visibility

Two entries surface in the automatic_decisions section of --report output:

  • VirtualOverrideBridged — the count of renamed virtual overrides successfully wired up. A sanity check that virtual renaming is firing on your code.
  • VirtualOverrideChainFrozen — override chains Demeanor had to keep at original names because their shape made renaming unsafe (typically unusual reflection patterns). Each entry lists the specific group and the reason, so you can see which methods didn’t rename and decide whether to restructure or add annotations.

When to Disable

  • Reflection on method names without a typeof(T).GetMethod(...) pattern — when your reflection code stores the System.Type in a variable or receives it from GetType(), Demeanor cannot resolve the target precisely and falls back to preserving every method with that name across the assembly set. Correct at runtime, but over-conservative; disable virtual rename only if this is pervasive.
  • Expression treesExpression.Call(typeof(T), "ToString", ...) uses string-based method lookup against the renamed method’s current name. Use the MethodInfo overload of Expression.Call instead, or exclude the affected methods via [Obfuscation].
  • Interop with unobfuscated assemblies that look up your methods by name — if external code binds to a specific method name on your type beyond standard virtual dispatch (e.g., late binding from PowerShell, VBA, COM without a type library), the renamed name won’t match.

Ready to protect your .NET code?

View Pricing All Options