PerfidiousLeaf
PerfidiousLeaf
CC#
Created by PerfidiousLeaf on 12/4/2024 in #help
Dynamically Implementing Interfaces at Runtime
Good day all, For a modding framework refactor/rework, we are implementing interface-based events, which can be defined by mods and will be loaded at runtime as well, ie:
public interface IEventUpdate : IEvent
{
void Update(float deltaTime);
}

// Subcription is automatic for implementing types
public class SomeUpdateableModClass : IEventUpdate
{
void Update(float deltaTime) { /* do something */}
}
public interface IEventUpdate : IEvent
{
void Update(float deltaTime);
}

// Subcription is automatic for implementing types
public class SomeUpdateableModClass : IEventUpdate
{
void Update(float deltaTime) { /* do something */}
}
and an event publisher will follow a pattern as follows:
public class SomeModClass
{
private readonly EventService _eventService;
public SomeModClass(EventService eventService){ /* DI Injection */ }

void Pushupdate()
{
_eventService.PublishEvent<IEventUpdate>((subscriber) => subscriber.Update(20));
}
}
public class SomeModClass
{
private readonly EventService _eventService;
public SomeModClass(EventService eventService){ /* DI Injection */ }

void Pushupdate()
{
_eventService.PublishEvent<IEventUpdate>((subscriber) => subscriber.Update(20));
}
}
The issue we have is that we support Lua scripting via MoonSharp, which includes events. This was here before and we have the legacy API which must be supported as follows:
EventService.Add("IEventUpdate", "someIndentifierString", function(deltaTime)
-- do something
end)
EventService.Add("IEventUpdate", "someIndentifierString", function(deltaTime)
-- do something
end)
So, I've been investigating using some combination of Linq.Expression and ExpandoObject or dynamic to dynamically construct a runtime proxy type that implements the interface but searching has been kind of a pain because all major examples are for either .NET Framework and don't work in .NET Core, or they're for Mocking/Testing and are answered using Moq, Castle or some other testing-targeted framework. I'd like to avoid having to Emit an IL method dynamically. We have some of this already for dynamic hooking classes and maintain it has been a growing pain. What are my options here? Because this is my goal in pseudo code:
public delegate void LuaCsAction(params object[] args);

public class EventService : IEventService
{
// ... assume there're collections up here
public void Add(string eventId, string identifier, params LuaCsAction[] args)
{
var obj = new ExpandoObject();
var mArr = _eventTypes[eventId].GetMethods();
for(int i=0; i<mArr.Length; i++)
{
((IDictionary<string, object>)obj)[mArr[i].Name] = args[i];
}

_eventSubscribers[_eventTypes[eventId]][identifier] = obj; //yes, I'm ommitting the cast.
}
}
public delegate void LuaCsAction(params object[] args);

public class EventService : IEventService
{
// ... assume there're collections up here
public void Add(string eventId, string identifier, params LuaCsAction[] args)
{
var obj = new ExpandoObject();
var mArr = _eventTypes[eventId].GetMethods();
for(int i=0; i<mArr.Length; i++)
{
((IDictionary<string, object>)obj)[mArr[i].Name] = args[i];
}

_eventSubscribers[_eventTypes[eventId]][identifier] = obj; //yes, I'm ommitting the cast.
}
}
Anyways, any help is appreciated.
40 replies