-
Couldn't load subscription status.
- Fork 5.2k
Description
Description
Consider the following scenario:
- Create an interface dynamically
- Use DispatchProxy.Create() to create a proxy (using some arbitrary DispatchProxy-derived class)
- Repeat with (1)
The first time 1 and 2 are run, everything works. The second time they are run, you get an InvalidCastException.
Reproduction Steps
using System;
using System.Reflection.Emit;
using System.Reflection;
for (int i = 0; i < 2; i++)
{
var an = new AssemblyName("Test");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDomain");
var tb = moduleBuilder.DefineType("IDummy", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract, null);
var type = tb.CreateType();
Console.WriteLine(type);
var method = typeof(DispatchProxy).GetMethod(nameof(DispatchProxy.Create), BindingFlags.Static | BindingFlags.Public)!
.MakeGenericMethod(new[] { type!, typeof(HelperProxy) });
var proxy = method.Invoke(null, new object[] { });
Console.WriteLine(proxy);
}
public class HelperProxy : DispatchProxy
{
protected override object? Invoke(MethodInfo targetMethod, object[] args) => throw null!;
}Expected behavior
Output:
IDummy
generatedProxy_1
IDummy
generatedProxy_2
Actual behavior
Output:
IDummy
generatedProxy_1
IDummy
Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.InvalidCastException: Unable to cast object of type 'generatedProxy_2' to type 'IDummy'.
at System.Reflection.DispatchProxy.Create[T,TProxy]()
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Program.<Main>$(String[] args) in C:\Sources\TestWcf\DispatchProxyFailure\Program.cs:line 15
Regression?
No response
Known Workarounds
Not regenerating the interface, but reusing the once created Type. However, this is not feasible in real world applications in all situations.
Another workaround is to make sure that name of the dynamically generated assembly is unique (e.g. in this example by adding the loop counter value).
Configuration
Reproduced with Release/Debug builds with .NET 6.0 and .NET 7.0.
Other information
It looks as if there is some caching going on. For example, if you generate the type once and repeatedly generate a proxy for that, the output is as follows:
IDummy
generatedProxy_1
IDummy
generatedProxy_1
So despite calling DispatchProxy.Create again, it returns the same type. Looking at the source there are some static dictionaries involved (in DispatchProxyGenerator).
It looks like that for the newly generated IDummy (loop n=1) it does recognize it is a different (interface) type (not taking the proxy from the dictionary.
Why the cast then fails here:
public static T Create<[DynamicallyAccessedMembers(T, TProxy>() where TProxy : DispatchProxy
{
return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T));
}is beyond me. It almost looks as if T is not "the same" as typeof(T).