此代码片段是我的类生成代码的简化摘录,它创建了两个类,它们作为泛型类型中的参数相互引用:
namespace Sandbox
{
using System;
using System.Reflection;
using System.Reflection.Emit;
internal class Program
{
private static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Test");
var typeOne = module.DefineType("TypeOne", TypeAttributes.Public);
var typeTwo = module.DefineType("TypeTwo", TypeAttributes.Public);
typeOne.DefineField("Two", typeof(TestGeneric<>).MakeGenericType(typeTwo), FieldAttributes.Public);
typeTwo.DefineField("One", typeof(TestGeneric<>).MakeGenericType(typeOne), FieldAttributes.Public);
typeOne.CreateType();
typeTwo.CreateType();
Console.WriteLine("Done");
Console.ReadLine();
}
}
public struct TestGeneric<T>
{
}
}
哪个应该产生相当于以下的MSIL:
public class TypeOne
{
public Program.TestGeneric<TypeTwo> Two;
}
public class TypeTwo
{
public Program.TestGeneric<TypeOne> One;
}
但是在行typeOne.CreateType()
上抛出此异常:
System.TypeLoadException was unhandled
Message=Could not load type 'TypeTwo' from assembly 'Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Source=mscorlib
TypeName=TypeTwo
StackTrace:
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateType()
at Sandbox.Program.Main(String[] args) in C:\Users\aca1\Code\Sandbox\Program.cs:line 20
有趣的事情要注意:
One
上定义字段TypeTwo
,则在TypeOne
之前创建TypeTwo
仍然失败,但在TypeTwo
成功之前创建TypeOne
。因此,异常是由于在通用字段类型中使用尚未创建的类型而引起的;但是,因为我需要使用循环引用,所以我无法通过按特定顺序创建类型来避免这种情况。TestGeneric<>
并将字段声明为TypeOne
&amp; TypeTwo
直接不会产生此错误;因此我可以使用已定义但未创建的动态类型。TestGeneric<>
从struct
更改为class
不会产生此错误;所以这种模式 适用于大多数泛型,而不是通用值类型。TestGeneric<>
的声明,因为它是在另一个程序集中声明的 - 特别是在System.Data.Linq.dll中声明的System.Data.Linq.EntityRef<>
。TestGeneric<>
作为嵌套类型,因此它继承了internal
可见性。我现在已经在上面的代码示例中解决了这个问题,它确实可以正常工作。关于a)为什么发生这种情况的任何想法,b)我如何解决这个问题和/或c)我如何解决它?
感谢。
答案 0 :(得分:10)
我不知道为什么会发生这种情况。我猜得很好。
正如您所观察到的,创建泛型类的方式与创建泛型结构的方式不同。当您创建类型'TypeOne'时,发射器需要创建泛型类型'TestGeneric',并且由于某种原因需要正确的Type而不是TypeBuilder。当尝试确定新通用结构的大小时,可能会发生这种情况?我不确定。也许TypeBuilder无法确定其大小,因此需要创建“TypeTwo”类型。
当找不到TypeTwo时(因为它只作为TypeBuilder存在),将触发AppDomain的TypeResolve事件。这使您有机会解决问题。在处理TypeResolve事件时,您可以创建类型“TypeTwo”并解决问题。
这是一个粗略的实现:
namespace Sandbox
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
internal class Program
{
private static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Test");
var typeOne = module.DefineType("TypeOne", TypeAttributes.Public);
var typeTwo = module.DefineType("TypeTwo", TypeAttributes.Public);
typeOne.DefineField("Two", typeof(TestGeneric<>).MakeGenericType(typeTwo), FieldAttributes.Public);
typeTwo.DefineField("One", typeof(TestGeneric<>).MakeGenericType(typeOne), FieldAttributes.Public);
TypeConflictResolver resolver = new TypeConflictResolver();
resolver.AddTypeBuilder(typeTwo);
resolver.Bind(AppDomain.CurrentDomain);
typeOne.CreateType();
typeTwo.CreateType();
resolver.Release();
Console.WriteLine("Done");
Console.ReadLine();
}
}
public struct TestGeneric<T>
{
}
internal class TypeConflictResolver
{
private AppDomain _domain;
private Dictionary<string, TypeBuilder> _builders = new Dictionary<string, TypeBuilder>();
public void Bind(AppDomain domain)
{
domain.TypeResolve += Domain_TypeResolve;
}
public void Release()
{
if (_domain != null)
{
_domain.TypeResolve -= Domain_TypeResolve;
_domain = null;
}
}
public void AddTypeBuilder(TypeBuilder builder)
{
_builders.Add(builder.Name, builder);
}
Assembly Domain_TypeResolve(object sender, ResolveEventArgs args)
{
if (_builders.ContainsKey(args.Name))
{
return _builders[args.Name].CreateType().Assembly;
}
else
{
return null;
}
}
}
}