For example I have some dll SomeLib
with the following classes:
public class Class1
{
public Class2 GetClass2() => new Class2();
}
public class Class2
{
public int Prop1 { get; set; } = 5;
}
I read it by Mono.Cecil:
var sourceType = typeof(Class1);
var assemblyDefinition = AssemblyDefinition.ReadAssembly(sourceType.Module.FullyQualifiedName);
Now I have Class1 and Class2 in the loaded assembly.
I would like to preserve Class1 in this "Mono.Cecil" assembly but delete Class2, just load Class2 from existing SomeLib.dll
by reference.
Here is my attempt.
It prints 5
. But if I uncomment //Delete Class2
section then it fails with exception.
static void Main(string[] args)
{
var sourceType = typeof(Class1);
var assemblyDefinition = AssemblyDefinition.ReadAssembly(sourceType.Module.FullyQualifiedName);
using (var stream = new MemoryStream())
{
var type = assemblyDefinition.MainModule.GetType(sourceType.FullName, true);
type.Name += "Custom";
assemblyDefinition.Name.Name += "Custom";
// Delete Class2
//var class2Type = assemblyDefinition.MainModule.Types.Single(t => t.FullName == typeof(Class2).FullName);
//assemblyDefinition.MainModule.Types.Remove(class2Type);
// Try to reference Class2's dll, SomeLib.dll
assemblyDefinition.MainModule.AssemblyReferences.Add(AssemblyNameReference.Parse(typeof(Class2).Assembly.FullName));
assemblyDefinition.Write(stream);
var assembly = Assembly.Load(stream.ToArray());
var newType = assembly.GetType(sourceType.FullName + "Custom");
var instance = Activator.CreateInstance(newType);
var method = newType.GetMethod(nameof(Class1.GetClass2));
var class2 = method.Invoke(instance, new object[0]);
var prop = class2.GetType().GetProperty(nameof(Class2.Prop1));
Console.WriteLine(prop.GetValue(class2));
}
Console.ReadLine();
}
Just to put the right answer - it is possible. Like Evk
said you can just replace all the references to Class2, something like this
_typeRef = assemblyDefinition.MainModule.ImportReference(typeof(Class2));
static void Visit(MethodDefinition method)
{
if (method.ReturnType.FullName == _typeRef.FullName)
{
method.ReturnType = _typeRef;
}
for (var i = 0; i < method.Parameters.Count; i++)
{
if (method.Parameters[i].ParameterType.FullName == _typeRef.FullName)
{
method.Parameters[i].ParameterType = _typeRef;
}
}
foreach (var cmd in method.Body.Instructions)
{
if (cmd.OpCode == OpCodes.Call || cmd.OpCode == OpCodes.Callvirt)
{
var methodDefinition = ((MethodReference) cmd.Operand).Resolve();
if (methodDefinition.Module == _typeRef.Module)
{
Visit(methodDefinition);
}
}
}
}
But, of course, you have to check all the types and members