dotnet/runtime

Unhelpful error message for missing TypeBuilder.CreateType() call

Open

#101 322 ouverte le 20 avr. 2024

Voir sur GitHub
 (1 commentaire) (0 réactions) (0 assignés)C# (5 445 forks)batch import
area-System.Reflection.Emithelp wanted

Métriques du dépôt

Stars
 (17 886 stars)
Métriques de merge PR
 (Merge moyen 12j 11h) (661 PRs mergées en 30 j)

Description

Description

Sub-issue of https://github.com/dotnet/runtime/issues/101298.

There is an error in this code, but the error message that gets raised in response does nothing to help with debugging the problem. (Also, the error that exists arguably shouldn't be an error, but that's a whole other issue.)

Reproduction Steps

using System.Reflection.Emit;
using System.Reflection;

namespace NestedTypeError
{
	internal class Program
	{
		static void Main(string[] args)
		{
			try
			{
				var type = BuildType();
				var method = type.GetMethod("Invalid")!;
				var result = method.Invoke(null, []);
				Console.WriteLine(result);
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
		}

		private static Type BuildType()
		{
			AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new("MyAssembly"), AssemblyBuilderAccess.RunAndCollect);
			ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyAssembly");
			var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public, typeof(ValueType));

			var nestedTypeBuilder = typeBuilder.DefineNestedType("InnerType", TypeAttributes.NestedPrivate);
			var ctor = nestedTypeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

			var value = nestedTypeBuilder.DefineMethod("Value", MethodAttributes.Public, typeof(int), []);
			var gen = value.GetILGenerator();
			gen.Emit(OpCodes.Ldc_I4_1);
			gen.Emit(OpCodes.Ret);
			//nestedTypeBuilder.CreateType();

			var invalid = typeBuilder.DefineMethod("Invalid", MethodAttributes.Public | MethodAttributes.Static, typeof(int), []);
			var il = invalid.GetILGenerator();
			il.Emit(OpCodes.Newobj, ctor);
			il.Emit(OpCodes.Callvirt, value);
			il.Emit(OpCodes.Ret);

			return typeBuilder.CreateType();
		}
	}
}

Expected behavior

This will error out on the call to method.Invoke. A useful error message giving some clue as to the nature of the problem that would meaningfully aid in debugging it is expected.

Actual behavior

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.TypeLoadException: Could not load type 'InnerType' from assembly 'MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at MyType.Invalid()
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at NestedTypeError.Program.Main(String[] args) in C:\Users\mason\source\repos\Repro2\NestedTypeError\NteProgram.cs:line 14

So the type InnerType cannot be loaded. OK. Why? What's wrong with it? In some cases, TypeLoadExeption gives a meaningful error message, but in this case, and several others I've run across, it does not.

The error here is that nestedTypeBuilder.CreateType() was never called. The error message really should reflect that.

(Also, should that even be necessary for a nested type? I can see decent arguments on both sides, but to me the .NET philosophy of "the pit of success" feels like the strongest argument. When I call typeBuilder.CreateType(), I intuitively expect it to create the entire type, with all of its declared members. Having it create "everything except for the nested types" feels inconsistent.)

Regression?

No response

Known Workarounds

No response

Configuration

.NET Core 8, Windows 10, x64.

Other information

No response

Guide contributeur