![]() |
VOOZH | about |
dotnet add package DanielWillett.ReflectionTools --version 4.1.0
NuGet\Install-Package DanielWillett.ReflectionTools -Version 4.1.0
<PackageReference Include="DanielWillett.ReflectionTools" Version="4.1.0" />
<PackageVersion Include="DanielWillett.ReflectionTools" Version="4.1.0" />Directory.Packages.props
<PackageReference Include="DanielWillett.ReflectionTools" />Project file
paket add DanielWillett.ReflectionTools --version 4.1.0
#r "nuget: DanielWillett.ReflectionTools, 4.1.0"
#:package DanielWillett.ReflectionTools@4.1.0
#addin nuget:?package=DanielWillett.ReflectionTools&version=4.1.0Install as a Cake Addin
#tool nuget:?package=DanielWillett.ReflectionTools&version=4.1.0Install as a Cake Tool
Shared library for generic reflection tools for CLR implementations. Tested on .NET Framework, .NET, and Mono.
NuGet Package: DanielWillett.ReflectionTools
Base module for ReflectionTools.
IOpCodeEmitter is an abstraction based off ILGenerator.
DebuggableEmitter implements this interface fully and logs any emitted instructions to Accessor.Logger (or the specified accessor).
It also has tools for adding 'breakpoints' to the method, which log every instruction to the logger as it executes in real-time.
DynamicMethod dynMethod = new DyanmicMethod(...);
IOpCodeEmitter emitter = dynMethod
.AsEmitter(debuggable: true, addBreakpoints: false)
.WithLogSource("...");
// also see EmitterExtensions below
emitter.Emit(OpCodes.Ldarg_0);
// ...
emitter.Emit(OpCodes.Ret);
Extensions that allow a safer and more readable way to emit instructions.
DynamicMethodInfo<Func<int, int, int>> dynMethod = DynamicMethodHelper.Create<Func<int, int, int>>("TryDivide");
IOpCodeEmitter emit = dynMethod.GetEmitter();
// *Add* Local and Label so you no longer have to mix up Declare and Define
emit.AddLocal<int>(out LocalBuilder lclResult)
.Try(emit =>
{
emit.LoadArgument(0)
.LoadArgument(1)
.Divide()
.SetLocalValue(lclResult);
})
.Catch<DivideByZeroException>(emit =>
{
emit.PopFromStack() // pop exception object
.SetLocalToDefaultValue<int>(lclResult);
})
.End()
.LoadLocalValue(lclResult)
.Return();
| OpCode | Extension Method |
|---|---|
| constrained. | Invoke |
| no. | unsupported |
| readonly. | LoadArrayElementAddress |
| tail. | Invoke |
| unaligned. | Most load-value functions |
| volatile. | Most load-value functions |
| try, leave | Try |
| catch, leave | Try(..).Catch |
| filter, endfilter | Try(..).CatchWhen |
| filter handler, leave | Try(..).CatchWhen(..).OnPass |
| finally, endfinally | Try(..).Finally |
| fault, endfault | Try(..).Fault |
| end try, leave | Try(..).Handler(..).End() |
| define label | AddLabel, AddLazyLabel (Lazy<Label>) |
| declare local | AddLocal |
| mark label | MarkLabel (Label, Lazy<Label>, or Label?) |
| add | Add |
| add.ovf | AddChecked |
| add.ovf.un | AddUnsignedChecked |
| and | And |
| arglist | LoadArgList |
| br | Branch |
| leave | Leave |
| beq | BranchIfEqual |
| bge | BranchIfGreaterOrEqual |
| bge.un | BranchIfGreaterOrEqualUnsigned |
| bgt | BranchIfGreater |
| bgt.un | BranchIfGreaterUnsigned |
| ble | BranchIfLessOrEqual |
| ble.un | BranchIfLessOrEqualUnsigned |
| blt | BranchIfLess |
| blt.un | BranchIfLessUnsigned |
| bne.un | BranchIfNotEqual |
| brfalse | BranchIfFalse |
| brtrue | BranchIfTrue |
| ceq | LoadIfEqual |
| cgt | LoadIfGreater |
| cgt.un | LoadIfGreaterUnsigned |
| clt → ldc.i4.0 → ceq | LoadIfGreaterOrEqual |
| clt.un → ldc.i4.0 → ceq | LoadIfGreaterOrEqualUnsigned |
| clt | LoadIfLess |
| clt.un | LoadIfLessUnsigned |
| cgt → ldc.i4.0 → ceq | LoadIfLessOrEqual |
| cgt.un → ldc.i4.0 → ceq | LoadIfLessOrEqualUnsigned |
| ckfinite | CheckFinite |
| box | Box |
| call, callvirt | Invoke |
| castclass | CastReference |
| conv.i | ConvertToNativeInt |
| conv.ovf.i | ConvertToNativeIntChecked |
| conv.ovf.i.un | ConvertToNativeIntUnsignedChecked |
| conv.u | ConvertToNativeUInt |
| conv.ovf.u | ConvertToNativeUIntChecked |
| conv.ovf.u.un | ConvertToNativeUIntUnsignedChecked |
| conv.i1 | ConvertToInt8 |
| conv.ovf.i1 | ConvertToInt8Checked |
| conv.ovf.i1.un | ConvertToInt8UnsignedChecked |
| conv.u1 | ConvertToUInt8 |
| conv.ovf.u1 | ConvertToUInt8Checked |
| conv.ovf.u1.un | ConvertToUInt8UnsignedChecked |
| conv.i2 | ConvertToInt16 |
| conv.ovf.i2 | ConvertToInt16Checked |
| conv.ovf.i2.un | ConvertToInt16UnsignedChecked |
| conv.u2 | ConvertToUInt16 |
| conv.ovf.u2 | ConvertToUInt16Checked |
| conv.ovf.u2.un | ConvertToUInt16UnsignedChecked |
| conv.i4 | ConvertToInt32 |
| conv.ovf.i4 | ConvertToInt32Checked |
| conv.ovf.i4.un | ConvertToInt32UnsignedChecked |
| conv.u4 | ConvertToUInt32 |
| conv.ovf.u4 | ConvertToUInt32Checked |
| conv.ovf.u4.un | ConvertToUInt32UnsignedChecked |
| conv.i8 | ConvertToInt64 |
| conv.ovf.i8 | ConvertToInt64Checked |
| conv.ovf.i8.un | ConvertToInt64UnsignedChecked |
| conv.u8 | ConvertToUInt64 |
| conv.ovf.u8 | ConvertToUInt64Checked |
| conv.ovf.u8.un | ConvertToUInt64UnsignedChecked |
| conv.r4 | ConvertToSingle |
| conv.r8 | ConvertToDouble |
| conv.r.un → conv.r4 | ConvertToSingleUnsigned |
| conv.r.un → conv.r8 | ConvertToDoubleUnsigned |
| cpblk | CopyBytes |
| cpobj | CopyValue |
| div | Divide |
| div.un | DivideUnsigned |
| dup | Duplicate |
| initblk | SetBytes |
| initobj | SetDefaultValue, SetLocalToDefaultValue |
| isinst | LoadIsAsType |
| jmp | JumpTo |
| ldarg | LoadArgument |
| ldarga | LoadArgumentAddress |
| ldc.i4 | LoadConstant[U]Int[8,16,32] |
| ldc.i4 | LoadConstantCharacter |
| ldc.i4.[0,1] | LoadConstantBoolean |
| ldc.i4.[0,1] | FailFilter, PassFilter (in filter only) |
| ldc.i8 | LoadConstant[U]Int64 |
| ldc.r4 | LoadConstantSingle |
| ldc.r8 | LoadConstantDouble |
| newobj decimal(,,,,) | LoadConstantDecimal |
| ldstr, ldnull | LoadConstantString |
| ldelem* | LoadArrayElement |
| ldelema* | LoadArrayElementAddress |
| ldfld | LoadInstanceFieldValue, LoadFieldValue |
| ldflda | LoadInstanceFieldAddress, LoadFieldAddress |
| ldsfld | LoadStaticFieldValue, LoadFieldValue |
| ldsflda | LoadStaticFieldAddress, LoadFieldAddress |
| ldftn | LoadFunctionPointer |
| ldvirtftn | LoadFunctionPointerVirtual |
| ldind, ldobj | LoadAddressValue |
| ldlen* | LoadArrayLength |
| ldloc | LoadLocalValue |
| ldloca | LoadLocalAddress |
| ldnull | LoadNullValue |
| ldtoken | LoadToken |
| ldtoken | LoadTypeOf (loads Type object) |
| localloc | StackAllocate, StackAllocate<T> |
| mkrefany | MakeTypedReference |
| refanytype | LoadTypedReferenceTypeToken |
| refanytype | LoadTypedReferenceType (loads Type object) |
| refanyval | LoadTypedReferenceAddress |
| refanyval | LoadTypedReferenceValue (loads value) |
| mul | Multiply |
| mul.ovf | MultiplyChecked |
| neg | Negate |
| not | BitwiseNot |
| ldc.i4.0 → ceq | Not |
| newarr* | CreateArray |
| newobj | CreateObject |
| nop | NoOperation |
| or | Or |
| pop | PopFromStack |
| rem | Modulo |
| rem.un | ModuloUnsigned |
| ret | Return |
| rethrow | Rethrow |
| shl | ShiftLeft |
| shr | ShiftRight |
| shr.un | ShiftRightUnsigned |
| sizeof | LoadSizeOf |
| starg | SetArgument |
| stelem* | SetArrayElement |
| stind, stobj | SetAddressValue |
| stfld | SetInstanceFieldValue, SetFieldValue |
| stsfld | SetStaticFieldValue, SetFieldValue |
| stloc | SetLocalValue |
| sub | Subtract |
| sub.ovf | SubtractChecked |
| sub.ovf.un | SubtractUnsignedChecked |
| switch | Switch |
| throw | Throw |
| unbox.any | LoadUnboxedValue |
| unbox | LoadUnboxedAddress |
| xor | Xor |
* Non-SZ arrays (multi-dimensional or non-zero bound arrays) use their type's Get, Set, and Address functions,
Length properties, or constructors instead of the native instruction which only works for vectors.
TranspileContext from DanielWillett.ReflectionTools.Harmony also implements IOpCodeEmitter.
Use the AddReflectionTools extension for IServiceCollection to add IReflectionToolsLogger, IOpCodeFormatter, and IAccessor as services.
Configures logging from the registered ILoggerFactory service.
Accessor.Formatter has methods for formatting members into strings efficiently and accurately. Can be swapped out for custom implementations made either from scratch or derived from DefaultOpCodeFormatter.
Formatting methods, fields, properties, types, parameters, and OpCodes.
public class C
{
public static void M<T>(scoped in T p, params int[] p2) where T : struct { }
}
// elsewhere
MethodInfo method = typeof(C).GetMethod("M", BindingFlags.Public | BindingFlags.Static);
string methodAsString = Accessor.Formatter.Format(method);
/*
* Value: 'static void C.M<T>(scoped in T p, params int[] p2)'
*/
Formatting member definitions
MethodDefinition method = new MethodDefinition("M")
.DeclaredIn<C>(isStatic: false)
.WithGenericParameterDefinition("T")
.WithParameter<int>("p")
.ReturningUsingGeneric("T",
elements: builder =>
{
builder.AddArray(2);
}
);
string methodAsString = Accessor.Formatter.Format(method);
/*
* Value: 'T[,] C.M<T>(int p)'
*/
See also Accessor.ExceptionFormatter, which is used for exceptions.
Contains an extension method for getting the milliseconds elapsed in a decimal form (not easily doable through normal methods) named GetElapsedMilliseconds.
Stopwatch sw = Stopwatch.StartNew();
// do stuff
sw.Stop();
Console.WriteLine($"Elapsed time: {sw.GetElapsedMilliseconds():F2} ms");
Expandable class filled with utilities for reflection.
Access private members quickly and easily using delegates.
Getting the value of static properties.
public class C
{
private static int P { get; private set; }
}
// elsewhere
StaticGetter<int> getter = Accessor.GenerateStaticPropertyGetter<C, int>("P", throwOnError: true)!;
int value = getter();
Setting instance fields, where the value and instance must be boxed.
internal struct C
{
private C F;
}
// elsewhere
// creates a delegate that accesses field 'F', working with boxed instances and values.
Type privateStruct = Type.GetType("C, A");
InstanceSetter<object, object?> setter = Accessor.GenerateInstanceSetter<object?>(privateStruct, "F", throwOnError: true)!;
object instance = Activator.CreateInstance(privateStruct);
object value = Activator.CreateInstance(privateStruct);
setter(instance, value);
Calling private methods.
public class C
{
private int M() { /* ... */ }
}
Func<C, int> caller = Accessor.GenerateInstanceCaller<C, Func<C, int>>("M", throwOnError: true, parameters: Type.EmptyTypes)
C instance = new C();
int returnValue = caller(instance);
extern.IsByRefLikeAttribute.IgnoreAttribute.PriorityAttribute, defaulting to 0.Action or Func for the given parameter info.object.ReflectionTypeLoadException.Invoke(...) method of a delegate type.FieldType, ReturnType, PropertyType, etc.callvirt'd instead of call'd at runtime. Doesn't account for future changes.callvirt'd instead of call'd.IOpCodeEmitter from an ILGenerator, DynamicMethod, or MethodBuilder.IVariable abstraction for fields and properties.
Both IAccessor and Variables contain methods for getting variables. The ones in IAccessor should be used in a DI environment, otherwise the ones in Variables will do.
// looks for an instance variable named "F" in class "C", returning a type-safe variable
IInstanceVariable<C, int>? variable = Variables.FindInstance<C, int>("F");
// looks for a static variable named "F" in class "C", returning a type-safe variable
IStaticVariable<int>? variable = Variables.FindStatic<C, int>("F");
// looks for a variable named "F" in class "C"
IVariable? variable = Variables.Find<C, int>("F");
The Operators class has utilities for finding operator methods in types.
Find<TDeclaringType>(OperatorType op, bool preferCheckedOperator = false)
Find<TLeft, TRight>(OperatorType op, bool preferCheckedOperator = false)
FindCast<TFrom, TTo>(OperatorType op, bool preferCheckedOperator = false)
MethodInfo for the conversion operator from one type to another.AllOperators { get; }
GetOperator(OperatorType)<OperatorName> { get; }
Operator) that has some basic information about the operator type.Requires Lib.Harmony 2.3.3+.
NuGet Package: DanielWillett.ReflectionTools.Harmony
Relies on DanielWillett.ReflectionTools
Lib.Harmony module for ReflectionTools. In versions before 3.0.0-prerelease1, this was part of the primary module.
HarmonyLog helps you keep an auto-clearing file log:
public static void Main(string[] args)
{
// reset the log on startup and configure Harmony to use the log.
string logFilePath = Path.Combine(Environment.CurrentDirectory, "harmony.log");
HarmonyLog.Reset(logFilePath);
}
It will be cleared on startup (not deleted, allowing any file editors to stay open).
You can also use HarmonyLog.ResetConditional, which is ignored if the compiler flag REFLECTION_TOOLS_ENABLE_HARMONY_LOG is not defined.
PatchUtility contains many helper methods for transpiling with a List<CodeInstruction> or a TranspilerContext object.
LocalBuilder or index of the local variable in an instruction.ExceptionBlockType, returning whether the type starts an exception block.ExceptionBlockType, returning whether the type ends an exception block.CodeInstruction to load a local variable.CodeInstruction to set a local variable.CodeInstruction to load a local variable's address.CodeInstruction to load a parameter value.CodeInstruction to set a parameter value.CodeInstruction to load a parameter's address.CodeInstruction to get the corresponding LocalReference.TranspilerContext can be used with PatchUtility in transpilers to simplify modifying methods and fetching existing members with reflection.
Partially implements IOpCodeEmitter.
The following transpiler replaces Console.WriteLine("Test {0}", "Value") with Accessor.Logger.LogInfo("Test Source", "Test Value").
public void TranspilerTarget()
{
if (1 == int.Parse("2"))
return;
Console.WriteLine("Test {0}", "Test2");
}
public static IEnumerable<CodeInstruction> WriteInstructions(IEnumerable<CodeInstruction> instructions, MethodBase method, ILGenerator generator)
{
TranspileContext ctx = new TranspileContext(method, generator, instructions);
MethodInfo? logInfo = typeof(IReflectionToolsLogger).GetMethod("LogInfo", BindingFlags.Public | BindingFlags.Instance, null, [ typeof(string), typeof(string) ], null);
if (logInfo == null)
{
return ctx.Fail(new MethodDefinition("LogInfo")
.DeclaredIn<IReflectionToolsLogger>(false)
.WithParameter<string>("source")
.WithParameter<string>("message")
);
}
MethodInfo? getLogger = typeof(Accessor).GetProperty("Logger", BindingFlags.Public | BindingFlags.Static)?.GetMethod;
if (getLogger == null)
{
return ctx.Fail(new PropertyDefinition("Logger")
.DeclaredIn(typeof(Accessor), true)
.WithNoSetter()
);
}
while (ctx.MoveNext())
{
if (PatchUtility.TryReplacePattern(ctx,
emit =>
{
emit.Invoke(getLogger)
.LoadConstantString("Test Source")
.LoadConstantString("Test Value")
.Invoke(logInfo);
},
new PatternMatch[]
{
x => x.LoadsConstant("Test {0}"),
x => x.LoadsConstant("Value"),
null
}
))
{
ctx.LogDebug("Patched arguments to LogInfo.");
}
}
return ctx;
}
Versions including and before v4.0.0 are licensed under GNU General Public License v3.0 only<br> Versions including and after v4.1.0 are licensed under GNU Lesser General Public License v3.0 or later.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 is compatible. net5.0-windows net5.0-windows was computed. net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 is compatible. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 is compatible. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 is compatible. |
| .NET Framework | net35 net35 is compatible. net40 net40 is compatible. net403 net403 was computed. net45 net45 is compatible. net451 net451 was computed. net452 net452 was computed. net46 net46 was computed. net461 net461 is compatible. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 is compatible. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 5 NuGet packages that depend on DanielWillett.ReflectionTools:
| Package | Downloads |
|---|---|
|
Uncreated.Warfare
Main framework for the Uncreated Warfare Military Simulation server based off of the military game Squad. |
|
|
DevkitServer.Client
Module for Unturned that enables multi-user map editing. |
|
|
DevkitServer.Server
Module for Unturned that enables multi-user map editing. |
|
|
DanielWillett.ModularRpcs
Uses reflection to auto-implement RPC send/receive functions to easily invoke functions on other machines. |
|
|
Uncreated.UI
Object-oriented wrapper for Unturned's effect-based server-side UI system. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 4.1.0 | 287 | 2/1/2026 | |
| 4.0.0 | 2,735 | 9/8/2024 | |
| 3.2.1 | 573 | 6/9/2024 | |
| 3.2.0 | 333 | 5/26/2024 | |
| 3.1.0 | 313 | 5/2/2024 | |
| 3.0.0 | 1,746 | 5/1/2024 | |
| 3.0.0-prerelease2 | 263 | 4/27/2024 | |
| 2.0.3 | 995 | 12/11/2023 | |
| 2.0.2 | 270 | 11/21/2023 | |
| 2.0.1 | 742 | 11/18/2023 | |
| 2.0.0 | 332 | 11/18/2023 | |
| 1.1.0 | 261 | 11/13/2023 | 1.1.0 is deprecated because it is no longer maintained. |
| 1.0.4 | 277 | 11/9/2023 | 1.0.4 is deprecated because it has critical bugs. |
| 1.0.3 | 284 | 11/8/2023 | 1.0.3 is deprecated because it has critical bugs. |
| 1.0.2 | 261 | 10/27/2023 | 1.0.2 is deprecated because it has critical bugs. |
| 1.0.1 | 284 | 10/21/2023 | 1.0.1 is deprecated because it has critical bugs. |
| 1.0.0 | 266 | 10/21/2023 | 1.0.0 is deprecated because it has critical bugs. |