mercredi 22 novembre 2023

What is the way of creating custom Reactive attribute?

I work with Reactive UI library for my WPF applications and was curious about how does the [Reactive] attribute work. I found out that there is nothing there in the attribute itself so there was a lot of questions about the actual work of it. Then I found the piece of code that loads current assembly (using Mono.Cecil and Fody libraries), finds all classes that extend ReactiveObject class and searches there for [Reactive] attributes (original source):

var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI);
var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray();
var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve() ?? throw new Exception("reactiveObjectExtensions is null");
var raiseAndSetIfChangedMethod = ModuleDefinition.ImportReference(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaiseAndSetIfChanged")) ?? throw new Exception("raiseAndSetIfChangedMethod is null");
var reactiveAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ReactiveAttribute", helpers) ?? throw new Exception("reactiveAttribute is null");

Then is creates private field for each property and binds a method (that calls PropertyChanged event).
Everything was quite clear so I decided to create my own attribute and property resolver for the attribute but now I have an obvious problem. When the resolver changes the IL code - it should rewrite the current assembly (as I understand)). I don't know much about prebuilds and postbuild things but I think that this things should be done not in the already running assembly but somewhere in prebuild actions. Here is the code I actually wrote (it crushes on the module.Write(Assembly.GetExecutingAssembly().Location); line (quite obvious cause the current assembly file is locked)):

var module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
var reactiveResolver = new ReactivePropertyResolver()
{
    ModuleDefinition = module,
};
reactiveResolver.Execute();
module.Write(Assembly.GetExecutingAssembly().Location);
module?.Dispose();

I changed almoust nothing in the ReactivePropertyResolver, only some namespaces and other small things.

So the main question is - Whereand how should I do all this stuff to get it work?

(I've already asked the question on Software Engineering SE but got downvoted - quite curious :/ )





Aucun commentaire:

Enregistrer un commentaire