diff --git a/Harmony/Public/CodeInstruction.cs b/Harmony/Public/CodeInstruction.cs
index e826b156..e9d7dc50 100644
--- a/Harmony/Public/CodeInstruction.cs
+++ b/Harmony/Public/CodeInstruction.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
@@ -11,7 +11,7 @@ namespace HarmonyLib
public class CodeInstruction
{
/// The opcode
- ///
+ ///
public OpCode opcode;
/// The operand
@@ -19,13 +19,18 @@ public class CodeInstruction
public object operand;
/// All labels defined on this instruction
- ///
+ ///
public List labels = new List();
/// All exception block boundaries defined on this instruction
- ///
+ ///
public List blocks = new List();
+ // Internal parameterless constructor that AccessTools.CreateInstance can use, ensuring that labels/blocks are initialized.
+ internal CodeInstruction()
+ {
+ }
+
/// Creates a new CodeInstruction with a given opcode and optional operand
/// The opcode
/// The operand
diff --git a/Harmony/Tools/AccessTools.cs b/Harmony/Tools/AccessTools.cs
index ba3fe8b4..b4990436 100644
--- a/Harmony/Tools/AccessTools.cs
+++ b/Harmony/Tools/AccessTools.cs
@@ -1692,12 +1692,26 @@ public static object CreateInstance(Type type)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
- var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[0], null);
+ var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, binder: null,
+ CallingConventions.Any, new Type[0], modifiers: null);
if (ctor is object)
- return Activator.CreateInstance(type);
+ return ctor.Invoke(null);
return FormatterServices.GetUninitializedObject(type);
}
+ /// Creates an (possibly uninitialized) instance of a given type
+ /// The class/type
+ /// The new instance
+ ///
+ public static T CreateInstance()
+ {
+ var instance = CreateInstance(typeof(T));
+ // Not using `as` operator since it only works with reference types.
+ if (instance is T typedInstance)
+ return typedInstance;
+ return default;
+ }
+
///
/// A cache for the or similar Add methods for different types.
///
@@ -1706,7 +1720,7 @@ public static object CreateInstance(Type type)
static readonly ReaderWriterLockSlim addHandlerCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// Makes a deep copy of any object
- /// The type of the instance that should be created
+ /// The type of the instance that should be created; for legacy reasons, this must be a class or interface
/// The original object
/// A copy of the original object but of type T
///
diff --git a/HarmonyTests/Tools/Assets/AccessToolsClass.cs b/HarmonyTests/Tools/Assets/AccessToolsClass.cs
index 1f0e12ed..2a71b457 100644
--- a/HarmonyTests/Tools/Assets/AccessToolsClass.cs
+++ b/HarmonyTests/Tools/Assets/AccessToolsClass.cs
@@ -168,6 +168,43 @@ public string Method1()
}
#pragma warning restore CS0169, CS0414, IDE0044, IDE0051, IDE0052
+ public static class AccessToolsCreateInstance
+ {
+ // Has default public parameterless constructor.
+ public class NoConstructor
+ {
+ public bool constructorCalled = true;
+ }
+
+ // Does NOT have a default public parameterless constructor (or any parameterless constructor for that matter).
+ public class OnlyNonParameterlessConstructor
+ {
+ public bool constructorCalled = true;
+
+ public OnlyNonParameterlessConstructor(int _)
+ {
+ }
+ }
+
+ public class PublicParameterlessConstructor
+ {
+ public bool constructorCalled = true;
+
+ public PublicParameterlessConstructor()
+ {
+ }
+ }
+
+ public class InternalParameterlessConstructor
+ {
+ public bool constructorCalled = true;
+
+ internal InternalParameterlessConstructor()
+ {
+ }
+ }
+ }
+
public static class AccessToolsMethodDelegate
{
public interface IInterface
diff --git a/HarmonyTests/Tools/TestAccessTools.cs b/HarmonyTests/Tools/TestAccessTools.cs
index bda283ee..2975e6cb 100644
--- a/HarmonyTests/Tools/TestAccessTools.cs
+++ b/HarmonyTests/Tools/TestAccessTools.cs
@@ -359,6 +359,18 @@ public void Test_AccessTools_GetDefaultValue()
Assert.AreEqual(null, AccessTools.GetDefaultValue(typeof(void)));
}
+ [Test]
+ public void Test_AccessTools_CreateInstance()
+ {
+ Assert.IsTrue(AccessTools.CreateInstance().constructorCalled);
+ Assert.IsFalse(AccessTools.CreateInstance().constructorCalled);
+ Assert.IsTrue(AccessTools.CreateInstance().constructorCalled);
+ Assert.IsTrue(AccessTools.CreateInstance().constructorCalled);
+ var instruction = AccessTools.CreateInstance();
+ Assert.NotNull(instruction.labels);
+ Assert.NotNull(instruction.blocks);
+ }
+
[Test]
public void Test_AccessTools_TypeExtension_Description()
{