Skip to content

Renaming a FieldDef in a generic class and updating all of its MemberRefs #477

@xyx0826

Description

@xyx0826

I am trying to rename a field in a generic class. Given this test class:

internal class GenericClass<T>
{
    private readonly T _value;

    public GenericClass(T value)
    {
        _value = value;
    }
}

I can locate and rename the _value field with this:

var genericType = mod.Types[2];
var genericField = genericType.Fields[0];
genericField.Name = "_valueRenamed";

And I can locate a MemberRef to a field with this name, as used in the class constructor, with:

var genericTypeCtorIl = genericType.Methods[0].Body.Instructions;
var genericFieldRef = genericTypeCtorIl[6].Operand as MemberRef;

With this a runtime exception occurs within the constructor because genericFieldRef still points to a field named _value. Only if I manually rename genericFieldRef will the code run correctly.

I've noticed that:

  • genericField.DeclaringType is a TypeDef GenericTest.GenericClass`1, while
  • genericFieldRef.DeclaringType is a TypeSpec GenericTest.GenericClass`1<T>.

I've also tried genericFieldRef.Resolve() in the hope of linking MemberRefs to FieldDefs but it returns null. I don't think I quite grasp the nuances around generic types. What would be a good way to resolve a generic MemberRef back to its FieldDef, or to consistently rename a generic field?

Edit - complete test code:

using System.Reflection;
using dnlib.DotNet;

namespace GenericTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            /* Rename GenericTest-renamed.exe back to GenericTest.exe
               Construct classes; the modified module will fail here */
            new GenericClass<string>(string.Empty);
            new NonGenericClass(string.Empty);

            /* Load current assembly */
            var path = Assembly.GetExecutingAssembly().Location;
            var opts = new ModuleCreationOptions(CLRRuntimeReaderKind.CLR);
            var modCtx = ModuleDef.CreateModuleContext();
            var mod = ModuleDefMD.Load(path, opts);
            mod.Context = modCtx;

            /* Locate and rename field in GenericClass */
            var genericType = mod.Types[2];
            var genericField = genericType.Fields[0];
            genericField.Name = "_valueRenamed";

            var genericTypeCtorIl = genericType.Methods[0].Body.Instructions;
            var genericFieldRef = genericTypeCtorIl[6].Operand as MemberRef;
            //genericFieldRef.Name = "_valueRenamed";     // Will crash if without

            /* Locate and rename field in NonGenericClass */
            var nonGenericType = mod.Types[3];
            var nonGenericField = nonGenericType.Fields[0];
            nonGenericField.Name = "_valueRenamed";

            //var nonGenericTypeCtorIl = nonGenericType.Methods[0].Body.Instructions;
            //var nonGenericFieldDef = nonGenericTypeCtorIl[6].Operand as FieldDef;   // Not needed

            mod.Write("GenericTest-renamed.exe");
        }
    }

    internal class GenericClass<T>
    {
        private readonly T _value;

        public GenericClass(T value)
        {
            _value = value;
        }
    }

    internal class NonGenericClass
    {
        private readonly object _value;

        public NonGenericClass(object value)
        {
            _value = value;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions