Washi1337/AsmResolver

Callbacks implementation for MemberCloner

LambdaTheDev opened this issue · 3 comments

Problem Description

When working with MemberCloner, you must do at least 2 loops through all copied types - implicit one (in MemberCloner), and explicit one (when you add types to module).

For small assemblies it won't matter, but for larger - it could cause huge processing time.

Callbacks system can solve this - user can define their callback types, cache processed types (for example interfaces), do some minor processing, and add result automatically to module - in a single iteration.

Proposal

To MemberCloner constructor, add argument Action<IMemberDescriptor>, and invoke it directly after the member is fully cloned.

Alternatives

No response

Additional Context

No response

I can definitely see value in this.

Perhaps as an alternative to calbacks we could also consider implementing a listener pattern for easier customization and case distinction from the user's perspective. This is particularly useful for users that just want to add their included types to the target module, and thus only need a case for TypeDefinition. Something like the following:

public class MyListener : MemberClonerListener
{
   public override void OnClonedMember(IMetadataMember original, IMetadataMember cloned)
   {
      // called for every member.
   }

   public override void OnClonedType(TypeDefinition original, TypeDefinition cloned)
   {
      // called for every type only
   }

   public override void OnClonedMethod(MethodDefinition original, MethodDefinition cloned)
   {
      // called for every method only.
   }

   // ...
}


var cloner = new MemberCloner(targetModule, new MyListener());

We can easily do both as well. The callback version is just a special case of a full listener version, as can be seen in a potential implementation below:

public class CallbackCloneListener : MemberClonerListener
{
   private readonly Action<IMetadataMember, IMetadataMember> _callback;

   public CallbackCloneListener(Action<IMetadataMember, IMetadataMember> callback)
   {
      _callback = callback;
   }

   public override void OnClonedMember(IMetadataMember original, IMetadataMember cloned) => _callback(original, cloned);
}

var cloner = new MemberCloner(targetModule, (original, cloned) => { ... }); 
// this would be shorthand for;
// var cloner = new MemberCloner(targetModule, new CallbackCloneListener((original, cloned) => { ... }));

Oh, MemberClonerListener idea is even better (split on types, methods, etc)!

Implemented in #337. This will be part of the 5.0 release.