I have class with the method:
class NetworkObject {
protected void LocalNetworkVariableChange(object value) {
// Do some stuff
}
}
I also have Mono.Cecil setup so that it injects a method call to this method in properties marked with a [NetworkVariable] attribute:
class Foo : NetworkObject {
public String Data {
get {
return ...
}
set {
// existing code here
...
// Injected call
this.LocalNetworkVariableChange(value);
}
}
This works fine using this code:
var propertySetMethod = property.SetMethod;
ILProcessor processor = propertySetMethod.Body.GetILProcessor();
var lastInstruction = propertySetMethod.Body.Instructions.Last();
var valueOperand = (FieldDefinition)propertySetMethod.Body.Instructions
.First(i => i.OpCode == OpCodes.Stfld)
.Operand;
var onLocalChangeMethod = assembly.MainModule
.Types.Single(td => td.FullName == typeof(NetworkObject).FullName)
.Methods.Single(md => md.Name == "LocalNetworkVariableChange")
.Resolve();
var newInstructions = new List<Instruction> {
// Load this on the evaluation step
Instruction.Create(OpCodes.Ldarg_0),
// Load to the stack the index of network variable
Instruction.Create(OpCodes.Ldc_I4, index),
// Loads the local variable at index 0 onto the evaluation stack.
Instruction.Create(OpCodes.Ldloc_0),
// Finds the value of a field in the object whose reference is currently on the evaluation stack.
Instruction.Create(OpCodes.Ldfld, valueOperand),
// Call the method
Instruction.Create(OpCodes.Callvirt, onLocalChangeMethod),
// Insert Nop before return
Instruction.Create(OpCodes.Nop)
};
foreach (var newInstruction in newInstructions) {
processor.InsertBefore(lastInstruction, newInstruction);
}
So whenever the setter is called, my method is called after. Now I need the same for fields, additionally to the properties.
But what can I do? Any idea? Can I convert the field to property and how?
The solution was quite simple: Every setter to the field is prolonged with a call to the method. I loop over all instructions of all members of all classes of all assemblies and search for
OpCodes.StdFldinstructions that set the field. Than right after the setter I inject the following calls.The boxing here is one part that I forgot in my initial question.