Getting Started with AbysmalCore Extensibility
The AbysmalCore Extensibility Framework provides a simple, uniform interface for dynamically loading, compiling, and interacting with C# code using reflection. This guide will walk you through the core concepts and steps for using the ExtensibilityHelper, UniformAssembly, UniformClass, UniformMethod, and UniformProperty classes.
1. Core Concepts
The framework revolves around wrapping the standard .NET Reflection types (Assembly, Type, MethodInfo, PropertyInfo, FieldInfo) into Uniform classes. This abstraction is designed to make common reflection tasks easier and more consistent.
| Uniform Class | Wraps | Description |
|---|---|---|
UniformAssembly |
Assembly |
Represents a compiled assembly, used to access its classes and invoke its entry point. |
UniformClass |
Type & object |
Represents an instantiated class from an assembly, providing access to its methods and properties. |
UniformMethod |
MethodInfo |
Represents a method, used for invoking it with arguments. |
UniformProperty |
PropertyInfo or FieldInfo |
Represents a property or field, used for getting and setting its value. |
2. Dynamic Compilation and Loading
The ExtensibilityHelper class is your starting point for turning raw C# source code into an executable assembly.
Step 1: Compiling Source Code
Use ExtensibilityHelper.CompileAssembly to compile a string of C# code into a standard System.Reflection.Assembly.
using System.Reflection;
using AbysmalCore.Extensibility;
string sourceCode = """
namespace MyExtension
{
public class MyClass
{
public string GetMessage() => "Hello from the dynamically loaded assembly!";
public int Count { get; set; } = 0;
}
}
""";
// internally we use CSharp.CodeAnalysis to compile
Assembly compiledAssembly = ExtensibilityHelper.CompileAssembly(sourceCode);
Step 2: Creating a Uniform Assembly
Once you have the Assembly, wrap it in a UniformAssembly to begin interacting with its contents.
// wrap the compiled assembly
// 'getPrivate = false' means only public members are exposed (default)
UniformAssembly uniformAssembly = ExtensibilityHelper.LoadAssembly(compiledAssembly);
bool hasClass = uniformAssembly.HasClass("MyExtension.MyClass"); // true
Important
Classes are found and retrieved using their full name. If your class is in a namespace, make sure to include its full path:
❌ MyClass
✅ MyExtension.MyClass
3. Interacting with Classes and Members
With the UniformAssembly, you can now instantiate classes and manipulate their methods and properties using the rest of the Uniform classes.
Step 3: Getting and Instantiating a Uniform Class
Retrieve the UniformClass by its name and get the instantiated object through the Instance property.
// get the wrapper for the class
UniformClass? myUniformClass = uniformAssembly.GetClass("MyExtension.MyClass");
if (myUniformClass != null)
{
// the Instance property holds an object of the underlying class (MyExtension.MyClass)
object myInstance = myUniformClass.Instance;
// you can create a new instance as well
object newInstance = myUniformClass.New();
}
Tip
If your wrapped class implements a common interface or abstraction, use UniformClass.DeriveFrom<T> to create an instance of that interface or abstraction with the class's overrides and implementations
Step 4: Invoking Methods
Use UniformClass.GetMethod to retrieve a method wrapper, then use its Invoke methods.
if (myUniformClass != null)
{
UniformMethod? getMessageMethod = myUniformClass.GetMethod("GetMessage");
if (getMessageMethod != null)
{
// invoke the method with no arguments
object? result = getMessageMethod.Invoke();
// invoke and cast the result
string message = getMessageMethod.Invoke<string>(); // "Hello from the dynamically loaded assembly!"
}
}
Step 5: Accessing Properties and Fields
Use UniformClass.GetProperty to retrieve a property/field wrapper, then use its Value property to get or set.
if (myUniformClass != null)
{
UniformProperty? countProperty = myUniformClass.GetProperty("Count");
if (countProperty != null)
{
// get the value (returns an object, so casting is often necessary)
int currentValue = (int)countProperty.Value!; // 0
// set the value
countProperty.Value = 42;
// verify new value
int newValue = (int)countProperty.Value!; // 42
}
}
4. Special Scenarios
Invoking an Assembly Entry Point
If your dynamically compiled assembly has a Main method (an entry point), you can invoke it directly.
// lets assume your source code has a public static void Main(string[] args)
object? output;
string[] cliArgs = { "arg1", "arg2" };
bool invoked = uniformAssembly.InvokeEntrypoint(cliArgs, out output);
if (invoked)
{
// 'output' will contain the result of the entry point method (e.g., an integer if it returns int)
}
Exposing Private Members
When creating a UniformAssembly or UniformClass, passing true for getPrivate will include non-public instance properties and fields in the resulting collections.
// this UniformClass instance will include private members
UniformClass privateClass = new UniformClass(myInstance, getPrivate: true);
// note: you can check if a member is private via the IsPrivate property