Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy am I getting this exception when emitting classes that reference each other via value-type generics?
    text
    copied!<p>This code snippet is a simplified extract of my class-generation code, which creates two classes that reference each other as arguments in a generic type:</p> <pre><code>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&lt;&gt;).MakeGenericType(typeTwo), FieldAttributes.Public); typeTwo.DefineField("One", typeof(TestGeneric&lt;&gt;).MakeGenericType(typeOne), FieldAttributes.Public); typeOne.CreateType(); typeTwo.CreateType(); Console.WriteLine("Done"); Console.ReadLine(); } } public struct TestGeneric&lt;T&gt; { } } </code></pre> <p>Which should produce MSIL equivalent to the following:</p> <pre><code>public class TypeOne { public Program.TestGeneric&lt;TypeTwo&gt; Two; } public class TypeTwo { public Program.TestGeneric&lt;TypeOne&gt; One; } </code></pre> <p>But instead throws this exception on the line <code>typeOne.CreateType()</code>:</p> <pre><code>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 </code></pre> <p>Interesting things to note:</p> <ul> <li>The circular reference isn't required to cause the exception; if I don't define field <code>One</code> on <code>TypeTwo</code>, creating <code>TypeOne</code> before <code>TypeTwo</code> still fails, but creating <code>TypeTwo</code> before <code>TypeOne</code> succeeds. Therefore, the exception is specifically caused by using a type that has not yet been created as an argument in a generic field type; however, because I need to use a circular reference, I cannot avoid this situation by creating the types in a specific order.</li> <li>Yes, I <em>do</em> need to use a circular reference.</li> <li>Removing the wrapper <code>TestGeneric&lt;&gt;</code> type and declaring the fields as <code>TypeOne</code> &amp; <code>TypeTwo</code> directly does not produce this error; thus I <em>can</em> use dynamic types that have been defined but not created.</li> <li>Changing <code>TestGeneric&lt;&gt;</code> from a <code>struct</code> to a <code>class</code> does not produce this error; so this pattern <em>does</em> work with most generics, just not generic value types.</li> <li>I can't change the declaration of <code>TestGeneric&lt;&gt;</code> in my case as it is declared in another assembly - specifically, <code>System.Data.Linq.EntityRef&lt;&gt;</code> declared in System.Data.Linq.dll.</li> <li>My circular reference is caused by representing two tables with foreign key references to each other; hence the need for that specific generic type and this specific pattern.</li> <li>Changing the circular reference to a self-reference <strong>edit</strong> succeeds. This failed originally because I had <code>TestGeneric&lt;&gt;</code> as a nested type in Program, so it inherited the <code>internal</code> visibility. I've fixed this now in the code sample above, and it does in fact work.</li> <li>Compiling the generated code manually (as C# code) also works, so it's not an obscure compiler issue.</li> </ul> <p>Any ideas on a) why this occuring, b) how I can fix this and/or c) how I can work around it?</p> <p>Thanks.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload