com.IvanMurzak.ReflectorNet 4.0.0

dotnet add package com.IvanMurzak.ReflectorNet --version 4.0.0
                    
NuGet\Install-Package com.IvanMurzak.ReflectorNet -Version 4.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="com.IvanMurzak.ReflectorNet" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="com.IvanMurzak.ReflectorNet" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="com.IvanMurzak.ReflectorNet" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add com.IvanMurzak.ReflectorNet --version 4.0.0
                    
#r "nuget: com.IvanMurzak.ReflectorNet, 4.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package com.IvanMurzak.ReflectorNet@4.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=com.IvanMurzak.ReflectorNet&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=com.IvanMurzak.ReflectorNet&version=4.0.0
                    
Install as a Cake Tool

ReflectorNet

NuGet netstandard2.1 .NET 8.0 .NET 9.0 Tests

Stars Discord License Stand With Ukraine

ReflectorNet is a sophisticated .NET reflection toolkit designed to bridge the gap between static .NET applications and dynamic, AI-driven environments. It provides robust serialization, intelligent method discovery, and dynamic invocation capabilities that allow AI agents to interact with .NET codebases safely and effectively.

🚀 Why ReflectorNet?

Traditional reflection is brittle and requires exact matches. ReflectorNet is built for flexibility:

  • 🤖 AI-Ready: Designed for scenarios where inputs (from LLMs) might be partial or fuzzy.
  • 🔍 Fuzzy Matching: Discover methods and types even with incomplete names or parameters (configurable match levels 0-6).
  • 📦 Type-Safe Serialization: Preserves full type information, supporting complex nested objects, collections, and custom types.
  • 🔄 In-Place Modification: Update existing object instances from serialized data without breaking references.
  • 📄 JSON Schema Generation: Automatically generate schemas for your types and methods to feed into LLM context windows.

📦 Installation

dotnet add package com.IvanMurzak.ReflectorNet

⚡ Quick Start

1. Setup

The Reflector class is your main entry point.

using com.IvanMurzak.ReflectorNet;

var reflector = new Reflector();

2. Serialization

Convert any .NET object into a SerializedMember intermediate representation. This preserves type metadata that standard JSON serializers might lose.

var myObject = new MyComplexClass { Id = 1, Name = "Test" };

// Serialize to intermediate representation
SerializedMember serialized = reflector.Serialize(myObject);

// Convert to JSON string if needed
string json = reflector.JsonSerializer.Serialize(serialized);

3. Deserialization

Reconstruct objects with full type fidelity.

// Restore to a specific type
MyComplexClass restored = reflector.Deserialize<MyComplexClass>(serialized);

// Or let Reflector resolve the type automatically
object restoredObj = reflector.Deserialize(serialized);

4. In-Place Modification

Update an existing object instance with new data. This is crucial for maintaining object identity in stateful applications (like Unity games or long-running services).

var existingInstance = new MyComplexClass();

// Modify 'existingInstance' with data from 'serialized'
// Returns true if successful
bool success = reflector.TryModify(ref existingInstance, serialized);

5. Dynamic Method Invocation

Allow AI to find and call methods without knowing the exact signature.

using com.IvanMurzak.ReflectorNet.Model;

// 1. Define what we are looking for (can be partial)
var methodRef = new MethodRef
{
    TypeName = "Calculator",
    MethodName = "Add", // Could be "AddValues" or "CalculateAdd" depending on match level
    InputParameters = new List<MethodRef.Parameter>
    {
        new MethodRef.Parameter { Name = "a", Value = "10" },
        new MethodRef.Parameter { Name = "b", Value = "20" }
    }
};

// 2. Call the method
// Note: We pass 'reflector' as the first argument to handle internal deserialization context
string result = reflector.MethodCall(
    reflector,
    methodRef,
    methodNameMatchLevel: 3, // Allow fuzzy matching
    executeInMainThread: false // Set to false for console apps/services (no UI thread)
);

Console.WriteLine(result); // Output: [Success] 30

6. Method Inspection & Schema Generation

Generate JSON schemas for types and methods to help LLMs understand your code structure.

// 1. Get schema for a specific type
var typeSchema = reflector.GetSchema<MyComplexClass>();

// 2. Get schema for method arguments (ideal for LLM function calling definitions)
var methodInfo = typeof(Calculator).GetMethod("Add");
var argsSchema = reflector.GetArgumentsSchema(methodInfo);

// 3. Get schema for method return value
var returnSchema = reflector.GetReturnSchema(methodInfo);

🏗️ Architecture

ReflectorNet is built on a Chain of Responsibility pattern to handle the complexity of .NET types.

Core Components

  • Reflector: The orchestrator. It manages the registry of converters and exposes the high-level API.
  • Registry: Holds a prioritized list of IReflectionConverters. When you serialize or deserialize, the registry finds the best converter for the specific type.
  • SerializedMember: The universal data model. It represents any .NET object (primitive, class, array) in a serializable format that holds both value and type metadata.

Built-in Converters

ReflectorNet comes with a set of standard converters:

  1. PrimitiveReflectionConverter: Handles int, string, bool, DateTime, etc.
  2. ArrayReflectionConverter: Handles arrays (T[]) and generic lists (List<T>).
  3. GenericReflectionConverter<T>: The fallback for custom classes and structs.
  4. TypeReflectionConverter & AssemblyReflectionConverter: Specialized handling for System.Type and System.Reflection.Assembly.

Extensibility

You can create custom converters for your own types by implementing IReflectionConverter or inheriting from GenericReflectionConverter<T> and registering them:

reflector.Converters.Add(new MyCustomConverter());

🛠️ Advanced Features

JSON Schema Generation

Generate schemas to describe your C# types to an LLM.

// Get schema for a type
var typeSchema = reflector.GetSchema<MyClass>();

// Get schema for method arguments (great for function calling)
var methodSchema = reflector.GetArgumentsSchema(myMethodInfo);

🧩 The Converter System (Custom Serialization)

ReflectorNet's power lies in its extensible Converter System. If you have "exotic" data models (e.g., third-party types you can't modify, complex graphs, or types needing special handling like System.Type), you can write a custom converter.

How it Works
  1. Interface: All converters implement IReflectionConverter.
  2. Base Class: Most custom converters should inherit from BaseReflectionConverter<T> or GenericReflectionConverter<T>.
  3. Priority: ReflectorNet asks every registered converter: "Can you handle this type, and how well?" (via SerializationPriority). The one with the highest score wins.
    • Exact match: Highest priority.
    • Inheritance match: Lower priority (based on distance).
    • No match: Zero.
Creating a Custom Converter

Here is an example of a converter for a hypothetical ThirdPartyWidget that should be serialized as a simple string instead of a complex object.

<details> <summary>Click to see the code example</summary>

using com.IvanMurzak.ReflectorNet.Converter;
using com.IvanMurzak.ReflectorNet.Model;

// 1. Inherit from GenericReflectionConverter<T> for the target type
public class WidgetConverter : GenericReflectionConverter<ThirdPartyWidget>
{
    // 2. Override SerializationPriority if you need special matching logic
    // (The default implementation already handles inheritance distance perfectly)

    // 3. Override InternalSerialize to customize output
    protected override SerializedMember InternalSerialize(
        Reflector reflector,
        object? obj,
        Type type,
        string? name = null,
        bool recursive = true,
        BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
        int depth = 0,
        Logs? logs = null,
        ILogger? logger = null,
        SerializationContext? context = null)
    {
        if (obj is ThirdPartyWidget widget)
        {
            // Serialize as a simple string value instead of an object with fields
            return SerializedMember.FromValue(
                reflector,
                type,
                value: $"Widget:{widget.Id}",
                name: name
            );
        }

        return base.InternalSerialize(reflector, obj, type, name, recursive, flags, depth, logs, logger, context);
    }

    // 4. Override CreateInstance if the type has no parameterless constructor
    public override object? CreateInstance(Reflector reflector, Type type)
    {
        return new ThirdPartyWidget("default-id");
    }
}

// 5. Register it
reflector.Converters.Add(new WidgetConverter());

</details>

Lazy Loading & Optional Dependencies

Sometimes you need to handle types that might not be present at runtime (e.g., optional plugins or platform-specific referencing like UnityEngine.Collider which is only available inside Unity). If you reference these types directly in your code, your application might crash if the assembly is missing.

LazyReflectionConverter solves this by resolving the type by its string name at runtime. If the type is found, it works; if not, it gracefully steps aside.

Basic Usage:

// Only active if "Some.Optional.Library.SuperWidget" exists at runtime
var lazyConverter = new LazyReflectionConverter("Some.Optional.Library.SuperWidget");

reflector.Converters.Add(lazyConverter);

Advanced Usage: Delegation & Ignoring Members

You can also use LazyReflectionConverter to wrap your own custom converters. This allows you to apply your custom logic only when the type exists, without taking a hard dependency on it.

// 1. Create your custom converter (assuming it can compile without the hard dependency, e.g. using generics or object)
// Or, if you have a converter that specific to a type but you want to lazy load it:
var myCustomLogic = new MySpecialConverter(); // Implements IReflectionConverter

// 2. Wrap it in LazyReflectionConverter
var lazyDelegate = new LazyReflectionConverter(
    "UnityEngine.Collider",
    backingConverter: myCustomLogic
);

reflector.Converters.Add(lazyDelegate);

You can also simply ignore specific properties or fields without writing a full custom converter:

// Ignore "heavyData" property if the type exists
var simpleLazy = new LazyReflectionConverter(
    "My.Optional.Type",
    ignoredProperties: new[] { "heavyData" }
);

📜 Custom JSON Schema Generation

While ReflectionConverter handles runtime object manipulation, you might also want to control how your types are described in the generated JSON Schema (used by LLMs to understand your data structure).

ReflectorNet allows you to customize this by implementing the IJsonSchemaConverter interface. This is often done by inheriting from JsonSchemaConverter<T>, which combines standard JSON serialization with schema generation.

How it Works
  1. Interface: Implement IJsonSchemaConverter.
  2. Registration: Add the converter to reflector.JsonSerializer.
  3. Generation: When reflector.GetSchema() is called, it checks if a registered converter exists for a type. If that converter implements IJsonSchemaConverter, it delegates schema creation to it.
Example: Custom Schema for a Widget

Suppose you have a ThirdPartyWidget that serializes to a string (e.g., "Widget:123"), and you want the LLM to know this format.

<details> <summary>Click to see the code example</summary>

using System.Text.Json;
using System.Text.Json.Nodes;
using com.IvanMurzak.ReflectorNet.Converter.Json;
using com.IvanMurzak.ReflectorNet.Utils;

// 1. Inherit from JsonSchemaConverter<T>
public class WidgetSchemaConverter : JsonSchemaConverter<ThirdPartyWidget>
{
    // 2. Define the Schema Definition (what the type looks like)
    public override JsonNode GetSchema()
    {
        return new JsonObject
        {
            [JsonSchema.Type] = "string",
            [JsonSchema.Pattern] = "^Widget:\\d+$",
            [JsonSchema.Description] = "A widget identifier in the format 'Widget:{id}'"
        };
    }

    // 3. Define the Schema Reference (how other types refer to it)
    public override JsonNode GetSchemaRef()
    {
        // Standard way to refer to the definition
        return new JsonObject
        {
            [JsonSchema.Ref] = JsonSchema.RefValue + TypeUtils.GetSchemaTypeId<ThirdPartyWidget>()
        };
    }

    // 4. Implement standard System.Text.Json logic (optional if only used for schema, but recommended)
    public override void Write(Utf8JsonWriter writer, ThirdPartyWidget value, JsonSerializerOptions options)
    {
        writer.WriteStringValue($"Widget:{value.Id}");
    }

    public override ThirdPartyWidget Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var str = reader.GetString();
        // Parse "Widget:123" back to object...
        return new ThirdPartyWidget(str.Split(':')[1]);
    }
}

// 5. Register it with the JSON Serializer
reflector.JsonSerializer.AddConverter(new WidgetSchemaConverter());

</details>

Note: You can use ReflectionConverter (for runtime logic) and JsonSchemaConverter (for schema/transport) together for the same type if needed.

Fuzzy Matching Levels

When searching for methods, you can tune the strictness:

  • 6: Exact match
  • 5: Case-insensitive match
  • 4: Starts with (Case-sensitive)
  • 3: Starts with (Case-insensitive)
  • 2: Contains (Case-sensitive)
  • 1: Contains (Case-insensitive)

🤝 Contributing

Contributions are welcome! Please submit Pull Requests to the main branch.

  1. Fork the repository.
  2. Create a feature branch.
  3. Commit your changes.
  4. Push to the branch.
  5. Open a Pull Request.

📄 License

This project is licensed under the Apache-2.0 License. Copyright - Ivan Murzak.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on com.IvanMurzak.ReflectorNet:

Package Downloads
com.IvanMurzak.McpPlugin.Common

McpPlugin common code for McpPlugin and McpPlugin.Server projects. It is .NET Library project for integration MCP server features into any dotnet application. It connects automatically with MCP server and exposes it's API over TCP connection in runtime. When MCP server interacts with AI.

com.IvanMurzak.McpPlugin.Server

MCP Server dotnet. Model Context Protocol server that interacts with MCP Plugin integrated into any dotnet application.

com.IvanMurzak.McpPlugin

McpPlugin is a .NET Library project for integration MCP server features into any dotnet application. It connects automatically with MCP server and exposes it's API over TCP connection in runtime. When MCP server interacts with AI. This library maintains a local application.

com.IvanMurzak.Unity.MCP.Common

Shared code between Unity-MCP-Plugin and Unity-MCP-Server projects.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on com.IvanMurzak.ReflectorNet:

Repository Stars
IvanMurzak/Unity-MCP
AI-powered bridge connecting LLMs and advanced AI agents to the Unity Editor via the Model Context Protocol (MCP). Chat with AI to generate code, debug errors, and automate game development tasks directly within your project.
Version Downloads Last Updated
4.0.0 280 3/3/2026
3.12.1 1,026 2/12/2026
3.12.0 345 1/31/2026
3.11.0 260 1/20/2026
3.10.0 192 1/19/2026
3.9.0 172 1/18/2026
3.8.1 155 1/18/2026
3.8.0 161 1/18/2026
3.7.1 210 1/17/2026
3.7.0 166 1/16/2026
3.6.0 229 1/10/2026
3.5.0 221 1/10/2026
3.4.0 179 1/8/2026
3.3.1 216 1/7/2026
3.3.0 211 1/5/2026
3.2.4 176 1/4/2026
3.2.3 176 1/4/2026
3.2.2 386 12/27/2025
3.2.1 181 12/27/2025
3.2.0 256 12/26/2025
Loading failed