23

I'm getting the following exception while trying to create a new instance of a class that heavily relies on generics:

new TestServer(8888);

System.TypeLoadException

GenericArguments[0], 'TOutPacket', on     
'Library.Net.Relay`4[TInPacket,TOutPacket,TCryptograph,TEndian]' 
violates the constraint of type parameter 'TInPacket'.

at System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, Int32 numGenericArgs, ObjectHandleOnStack type)
at System.RuntimeTypeHandle.Instantiate(Type[] inst)
at System.RuntimeType.MakeGenericType(Type[] instantiation)

I'm puzzled as to why this happens. Aren't generic constraints checked at compile time?

My googling brought me to the conclusion that this has something to do with either of these causes, or (sometimes?) both:

  • The order in which the generic constraints (where) are defined in the classes;
  • The use of the self-referencing generic pattern (conter-intuitive but very legal, see Eric Lippert's blog post)

One thing I am not ready to sacrifice is the self-referencing pattern. I absolutely need it for a specific purpose.

However, I'd like some help to point out where and why this problem occurs. As the library is massive and makes huge generic patterns, I think it would be best to progressively give code bits on request.

Upon request, declarations again. But I'd like to stress the fact that I would rather know generally why an exception like this can occur and then proceed to fix it myself in my specific code rather than find a specific fix, for posterity. Also, it will be much longer for anyone analyzing the code to answer than to give a general explanation as to why generic type constraints can be violated at runtime.

Implementation declarations:

class TestServer : Server<TestServer, TestClient, ServerPacket.In, ServerPacket.Out, BlankCryptograph, LittleEndianBitConverter>

class TestClient : AwareClient<TestOperationCode, TestServer, TestClient, ServerPacket.In, ServerPacket.Out, BlankCryptograph, LittleEndianBitConverter>

class ServerPacket
{
    public abstract class In : AwarePacket<TestOperationCode, TestServer, TestClient, ServerPacket.In, ServerPacket.Out, BlankCryptograph, LittleEndianBitConverter>.In
    public class Out : OperationPacket<TestOperationCode, LittleEndianBitConverter>.Out
}

public enum TestOperationCode : byte

Library declarations:

public abstract class Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian> : IDisposable
    where TServer : Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TClient : Client<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TInPacket : Packet<TEndian>.In
    where TOutPacket : Packet<TEndian>.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Relay<TInPacket, TOutPacket, TCryptograph, TEndian> : IDisposable
    where TInPacket : Packet<TEndian>.In
    where TOutPacket : Packet<TEndian>.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Client<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian> : Relay<TInPacket, TOutPacket, TCryptograph, TEndian>, IDisposable
    where TServer : Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TClient : Client<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TInPacket : Packet<TEndian>.In
    where TOutPacket : Packet<TEndian>.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Packet<TEndian> : ByteBuffer<TEndian>, IDisposable 
    where TEndian : EndianBitConverter, new()
{
    public abstract class In : Packet<TEndian>
    public abstract class Out : Packet<TEndian>
}

public class OperationPacket<TOperationCode, TEndian> 
    where TEndian : EndianBitConverter, new()
{
    public class In : Packet<TEndian>.In
    public class Out : Packet<TEndian>.Out
}

public abstract class AwareClient<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian> : Client<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>, IDisposable
    where TCryptograph : Cryptograph, new()
    where TInPacket : AwarePacket<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>.In
    where TOutPacket : Packet<TEndian>.Out
    where TServer : Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TClient : AwareClient<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TEndian : EndianBitConverter, new()

public class AwarePacket<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TCryptograph : Cryptograph, new()
    where TInPacket : AwarePacket<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>.In
    where TOutPacket : Packet<TEndian>.Out
    where TServer : Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TClient : AwareClient<TOperationCode, TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
    where TEndian : EndianBitConverter, new()
{
    public abstract class In : OperationPacket<TOperationCode, TEndian>.In
}

As noted in the comments, the simplest way to get help on this question for me would be to minimize the code to a small and reproducible example in which the bug is still present. However, this is both hard and long for me, and has the high chances of making the bug a heisenbug, as it occurs from complexity.

I tried to isolate it to the following, but I don't get the bug when I do:

// Equivalent of library
class A<TA, TB, TI, TO> // Client
    where TA : A<TA, TB, TI, TO>
    where TB : B<TA, TB, TI, TO>
    where TI : I
    where TO : O
{ }

class B<TA, TB, TI, TO> // Server
    where TA : A<TA, TB, TI, TO>
    where TB : B<TA, TB, TI, TO>
    where TI : I
    where TO : O
{ }

class I { } // Input packet

class O { } // Output packet

// Equivalent of Aware

class Ii<TA, TB, TI, TO> : I { } // Aware input packet

class Ai<TA, TB, TI, TO> : A<TA, TB, TI, TO> // Aware capable client
    where TA : Ai<TA, TB, TI, TO>
    where TB : B<TA, TB, TI, TO>
    where TI : Ii<TA, TB, TI, TO>
    where TO : O
{ }

// Equivalent of implementation

class XI : Ii<XA, XB, XI, XO> { }
class XO : O { }

class XA : Ai<XA, XB, XI, XO> { }
class XB : B<XA, XB, XI, XO> { }

class Program
{
    static void Main(string[] args)
    {
        new XB(); // Works, so bad isolation
    }
}

Gory Details

  1. Analyzing the exception tells us that TOutPacket violates TInPacket on Relay<TInPacket, TOutPacket, TCryptograph, Tendian>.
  2. The instance of Relay we have is TestClient, which implements AwareClient, which implements Client, which implements Relay.
    • AwareClient is used in conjunction with AwarePacket so that both ends are aware of which type of client receives which type of packets.
  3. Therefore, we know that TOutPacket in TestClient violates TInPacket in TestClient.
  4. The class implementing TOutPacket is ServerPacket.Out, which is a derivative of OperationPacket. This type is relatively simple in terms of generics, as it only provides an enum type and an endian type, making no cross-reference to other classes. Conclusion: The problem is not (most likely) not in this declaration by itself.
  5. The class implementing TInPacket is ServerPacket.In, which is a derivative of AwarePacket. This type is much more complex than TOutPacket, since it cross-references generics to be aware (AwarePacket) of the client that received it. It is probably in this generic mess that the problem occurs.

Then, many hypotheses can fuse. At this point, what I read is correct and accepted by the compiler, but there is evidently something wrong there.

Can you help me find out why I'm getting a generic constraint violation at runtime with my code?

George Stocker
  • 57,289
  • 29
  • 176
  • 237
Lazlo
  • 8,518
  • 14
  • 77
  • 116
  • Are you using reflection to specialize generic types? Or perhaps use a library that does? – CodesInChaos Jul 24 '11 at 20:25
  • 7
    As a side-note: I'm pretty sure you're overusing generics a bit. In particular the `TCryptograph` and `TEndian` type parameters strike me as odd. I think these should be normal properties of type `Cryptograph` and `EndianBitConverter` to which you assign an instance of a derived class. – CodesInChaos Jul 24 '11 at 20:27
  • 1
    Is your assembly verifiable? And where did your code go? You just had the declaration of your `Server` class posted. – CodesInChaos Jul 24 '11 at 20:28
  • I'd rather stay away from design questionability. For instance, `TEndian` was needed, but I could use TCryptograph otherwise. Only I found that it was more convenient like that. – Lazlo Jul 24 '11 at 20:29
  • I removed the declarations because I realized I'd have a lot to share, and thought this would be rather dissuasive as it would rather make people complain at the overall design rather than explaining why such an exception occurs. – Lazlo Jul 24 '11 at 20:31
  • And did you mix up your `TInPacket` and `TOutPacket` parameters somewhere? "`TOutPacket`, on ... violates the constraint of type parameter `TInPacket`" seems to indicate that. – CodesInChaos Jul 24 '11 at 20:32
  • 1
    @Lazlo: Can you post the stack trace(s)? – LukeH Jul 24 '11 at 20:38
  • @CodeInChaos: Added declarations, looking for swapping right now. But theoretically, it wouldn't let me compile. – Lazlo Jul 24 '11 at 20:44
  • The `AwarePacket` you posted is incomplete. It's missing the subclasses and it's missing the generic constraint on `TEndian`. And the concrete classes are missing too. – CodesInChaos Jul 24 '11 at 20:47
  • Repasted `AwarePacket`. Its only subclass in `In`. What concrete classes are you talking about? – Lazlo Jul 24 '11 at 21:01
  • I don't have the mental energy to analyse your code, but I suggest you look at [this post](http://stackoverflow.com/questions/6012420/why-does-this-generics-scenario-cause-a-typeloadexception). In that case, it was shown to be a (known) compiler bug. – Igby Largeman Jul 24 '11 at 22:31
  • Mm. If it's a bug, then my question now revolves on: how do I fix it? If not, I'll be waiting on a C# compiler developer to come and explain what went wrong. – Lazlo Jul 24 '11 at 22:58
  • More importantly: I have no interface! – Lazlo Jul 24 '11 at 22:59
  • True. And while my post did involve TypeLoadException too, your runtime error message is different from mine, so I guess this has to be a different issue. But the chance that it's a compiler bug is still good, because if there's one instance where the compiler and runtime disagree on generics, then there's probably others. I'd love to help but I'd need you to provide a compilable code example demonstrating the bug in the simplest possible form, cuz looking at what you've posted makes my eyes water. – Igby Largeman Jul 24 '11 at 23:42
  • @Charles: Unfortunately not all code is mine to share. Therefore, the first step we could go about is isolating a test case, then work from that. – Lazlo Jul 24 '11 at 23:46
  • Right, so can you do that? Distill it down to the *minimum* code needed to create the exception (a contrived example is fine), post it, and tell us which line the exception happens on. – Igby Largeman Jul 25 '11 at 16:17
  • @Charles: Will try to do that tonight, but it's a long process. Especially considering that distilling the code might create a heisenbug, as this bug seems to arise form complexity. – Lazlo Jul 25 '11 at 17:25
  • @Charles: Updated the question. Distilling the code really isn't easy. – Lazlo Jul 25 '11 at 22:19
  • @Lazlo, I don't see that you mentioned it, but are you on .Net 4? Also, can you post the *full* stack trace? – Kirk Woll Jul 27 '11 at 01:10
  • @Kirk Woll: I am on .NET 4, yes. Also, this is the full stack trace I could get through the Visual Studio debugger. – Lazlo Jul 27 '11 at 02:33
  • Cause found, please read my answer! – Lazlo Jul 27 '11 at 03:09

4 Answers4

14

Solution:

So, after some finagling with the generic parameters and constraints, I think I've finally found the issue/solution, and I hope I'm not celebrating too early.

First things first, I still think this is a bug (or at least a quirk) with how the dynamic runtime is trying to invoke the constructor of TestServer. It could also be a compiler bug, that is, if it is against the standard to convert a typed class to a dynamic (then I suppose back again) instead of casting it to its expected type.

By that, I mean that this code:

 TestServer test = new TestServer(GetPort());

turns into the Binder.InvokeConstructor below, doing a whole bunch of extra casting and looks nothing like the code you would expect it to be (the code below generated after an int cast would be expected)

On to the solution, it all has to do with the order of the generic arguments. As far as I know, there's nothing in the standard that has a say in what order you should put your generics in. The code works when you instantiate the class with a normal int. Take a look at how Server and Client have their arguments ordered:

 Client<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>
 Server<TServer, TClient, TInPacket, TOutPacket, TCryptograph, TEndian>

Exactly the same. If remove all the other classes from TestClient, and make TestClient's constraints only work with the base Client and Server class, everything works as expected, no exceptions. I've found the issue is with AwareClient and AwarePacket and the addition of TOperationCode

If you remove TOperationCode and on the abstract classes and the inheriting classes, the code again, works as expected. This is undesirable, as you probably want that generic argument in your class. I've found that moving it to the end of the arguments solves the problem.

 AwareClient<TOperationCode, TServer, TClient, 
             TInPacket, TOutPacket, TCryptograph, TEndian>
 AwarePacket<TOperationCode, TServer, TClient, TInPacket, 
             TOutPacket, TCryptograph, TEndian>

becomes

 AwareClient<TServer, TClient, TInPacket, TOutPacket, 
                   TCryptograph, TEndian, TOperationCode>
 AwarePacket<TServer, TClient, TInPacket, TOutPacket, 
                   TCryptograph, TEndian, TOperationCode>

Of course, you have to make a few more changes with the order of the generic constraints to get it to compile, but that seems to solve your issue.

That said, my gut feeling tells me this is a bug in the clr. Now, it's not just as simple as having 2 classes with the generic arguments out of order, or one that inherits from the other with an added argument. I'm working on trying to reproduce this with an simpler example, but so far, this one case is the only one I've been able to get an exception with.


EDIT(s)/My Discovery Process

If you remove the constraints in the Relay<TInPacket, TOutPacket, TCryptograph, TEndian> class, the exceptions are not thrown.

I think what I find more interesting is that the exceptions are only thrown the first time you try to create the TestClient, at least on my machine (these are still FirstChanceExceptions that are apparently handled by the internal runtime, they are unhandled by user code).

Doing this:

new TestServer(GetPort());
new TestServer(GetPort());
new TestServer(GetPort());

does not result in the same call via the dynamic method, but rather the compiler makes three separate CallSite classes internally, three separate declarations. This makes sense from an implementation standpoint. What I find especially interesting, though, is that even though, from what I can see, their code is not shared (who knows if it is internally), the exceptions are only being thrown the on the first call to the constructor.

I wish I had the ability to debug this, but Symbol Servers won't download the source for the dynamic builders, and the locals window is not very helpful. I'm hoping someone from Microsoft can help to answer this mystery.


I think I have it, but I'm not sure. I would definitely need an expert on C# dynamics to confirm this.

So, I did a few tests to figure out why it would fail with an explicit cast vs a implicit cast when passing it to TestServer constructor.

This is the main code for your version as compiled:

private static void Main(string[] args)
{
    if (<Main>o__SiteContainer0.<>p__Site1 == null)
    {
        <Main>o__SiteContainer0.<>p__Site1 = 
        CallSite<Func<CallSite, Type, object, TestServer>>.Create(
        Binder.InvokeConstructor(CSharpBinderFlags.None, typeof(Program),
        new CSharpArgumentInfo[] {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | 
            CSharpArgumentInfoFlags.UseCompileTimeType, null), 
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
    }

    TestServer server = <Main>o__SiteContainer0.<>p__Site1.Target.Invoke(
    <Main>o__SiteContainer0.<>p__Site1, typeof(TestServer), GetPort());
    Console.ReadLine();
}

Essentially, what is happening is that the RuntimeBinder has created a function is trying to create, not the int to pass to GetPort(), but instead a new TestServer, dynamically invoking its constructor.

Look at the difference when you cast it to an int and pass it to the constructor:

private static void Main(string[] args)
{
    if (<Main>o__SiteContainer0.<>p__Site1 == null)
    {
        <Main>o__SiteContainer0.<>p__Site1 = 
        CallSite<Func<CallSite, object, int>>.Create(Binder.Convert(
        CSharpBinderFlags.ConvertExplicit, typeof(int), typeof(Program)));
    }

    TestServer server = new TestServer(
    <Main>o__SiteContainer0.<>p__Site1.Target.Invoke(
    <Main>o__SiteContainer0.<>p__Site1, GetPort()));

    Console.ReadLine();
}

Notice instead of it creating a InvokeConstructor binding, it creates a Convert binding, with an Explicit flag. Instead of trying to dynamically invoke the constructor, it invokes a function that converts the dynamic to the TestServer constructor, thus passing it an actual int instead of a generic object.

I guess my point is that, there is definitely nothing wrong with your generics (aside from the fact they're fairly illegible and IMO overused), but rather an issue with how the compiler is trying to dynamically invoke the constructor.

Furthermore, it looks like it has nothing to do with actually passing along the int to the constructor. I removed the constructor from TestClient and made this CallSite, (essentially the same as the erroring one minus the int parameter)

        var lawl = CallSite<Func<CallSite, Type, TestServer>>.Create(
        Binder.InvokeConstructor(CSharpBinderFlags.None, typeof(Program), 
        new CSharpArgumentInfo[] { 
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | 
            CSharpArgumentInfoFlags.UseCompileTimeType, null), 
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));

        TestServer lol = lawl.Target.Invoke(lawl, typeof(TestServer));

And the same TypeLoadException, GenericArguments[0], 'TOutPacket', on 'ConsoleApplication1.Relay`4[TInPacket,TOutPacket,TCryptograph,TEndian]' violates the constraint of type parameter 'TInPacket'. occurred. Apparently the runtime is having a hard time invoking constructors on your generic type.

Seems like this might be a bug...


If you enable .NET Source browsing and enabling breakpoints on any thrown exception, you will catch the TypeLoadException. and can view the entire .net stack trace. Also, it is you can reproduce it with WinDbg.

Christopher Currens
  • 29,917
  • 5
  • 57
  • 77
  • What an amazing story, and my votes ended for today :-( – Dan Abramov Aug 01 '11 at 23:27
  • Definite +1, going to re-read that a couple of times though. EDIT: Added line breaks to improve readability, someone with enough rep please approve. – Lazlo Aug 02 '11 at 00:08
  • i think you're really onto something. i noticed when i was debugging that the type of the thing you get from new TestServer() is actually a dynamic also, not a TestServer. since testServer is it self used as a type arguement in the other classes, that could be the problem, as it might be some RuntimeType instead of an actual TestServer.. now the big question is why it seems to work for most people, maybe a subtle bug that has been fixed in the runtime, maybe something to do with type caching, who knows – aL3891 Aug 02 '11 at 08:01
  • That's a good question. Well, I do know that the exceptions thrown are FirstChanceExceptions that are caught successfully by the runtime. @Lazlo said he gets these exceptions in the debugger, so it could be that its set to break execution on all exceptions, thrown and/or unhandled. – Christopher Currens Aug 02 '11 at 16:17
  • Mm, your improved answer is even more interesting. Do you have any idea why the order of the generics matters in such a case? Based on the compiler code, I can't see any offhand. – Lazlo Aug 02 '11 at 17:59
  • EDIT: I'd also kind of be looking for a logical answer as to why the order of the generic parameters affects anything, a link to the spec in that case, if, of course, this whole thing is not a bug. If it is a bug, the explanation of why it does have an impact in the actual compiled code, regardless of the spec. – Lazlo Aug 02 '11 at 18:03
  • It shouldn't matter. Clearly it doesn't to the clr, because it works when you don't use dynamic.objects. This feels like a dynamic runtime binding issue, with trying to construct the class. – Christopher Currens Aug 02 '11 at 18:04
  • I gave you the bounty for the thorough analysis, but I'll still wait to see if someone @Microsoft has an explanation for the bug, which is the real "answer" to this problem. Many thanks, by the way. :) – Lazlo Aug 02 '11 at 22:20
8

It had nothing to do with all the generic constructs. Believe it or not, my design was stable and functional.

The actual cause was the only thing I didn't suspect: the int port parameter passed to new TestServer(int port).

This int was actually obtained through a dynamic expression which is irrelevant. Let's say it was

dynamic GetPort() { return 8888; }

new TestServer(GetPort()); // Crash
new TestServer((int)GetPort()); // Works

Apologies to CodeInChaos for saying I used no reflection, I guess that was only half-true.

Now, the bounty is started and the bug is still there (I want to use my dynamic method). So, could anyone a) explain why this happens (after all, the type is valid) and b) propose a way to fix it? Bounty and accepted answer will go to that person.

If you want to experiment, I got this code to reproduce and crash: http://pastie.org/2277415

If you want the actual executable that crashes, along with the solution and project: http://localhostr.com/file/zKKGU74/CrashPlz.7z

Lazlo
  • 8,518
  • 14
  • 77
  • 116
  • 1
    I'm not sure, it still doesn't crash for me. I converted all your code to a [standalone console app](http://jsfiddle.net/Gadmv/1/). Runs perfectly. So I guess it's still hard to isolate. – Kirk Woll Jul 27 '11 at 03:24
  • @Kirk Woll: I got this to crash: http://pastie.org/2277415 – Lazlo Jul 27 '11 at 03:48
  • 1
    The code you [posted](http://pastie.org/2277415) doesn't crash on my machine!. it is successfully continue to `Console.ReadLine()`. the problem still hiding elsewhere. – Jalal Said Jul 27 '11 at 04:40
  • @Jalal, me too. (I'm on Win7, 64-bit, .Net 4.0) – Kirk Woll Jul 27 '11 at 04:55
  • 1
    it *does* crash for me.. this is a head scratcher :P especially since TestServer doesnt have any open generics – aL3891 Jul 27 '11 at 14:27
  • It crashed for me on Windows 7, 64-bit, .NET 4.0, Visual C# Express 2010, Debug x86 build, running with debugger. – Lazlo Jul 27 '11 at 14:48
  • 2
    Doesn't crash for me on Win7 32bit, no service pack, running in VS2010 debug and release modes. Reading the code hurts my brain though! – Igby Largeman Jul 29 '11 at 05:16
  • No crash for me on Windows 7 64 bit (.NET 4 SP1) or Vista 32 bit (.NET 4 SP1). Will try again under Visual Studio... – Jon Skeet Jul 29 '11 at 20:13
  • @Jon Skeet: Added the executable. – Lazlo Aug 01 '11 at 01:15
  • @Lazlo: Just downloaded and run - it doesn't fail for me. Will try on another machine. Do you have 4.0 SP1 installed or not? – Jon Skeet Aug 01 '11 at 05:28
  • @Jon Skeet: TBH I can't recall if I have SP1 installed on my test machine. Will check tonight. – Lazlo Aug 01 '11 at 13:49
  • @Jon Skeet: Meanwhile, do you know of any change in the SP1 behaviour that might have been fixed? – Lazlo Aug 01 '11 at 13:58
  • @Lazlo: Not offhand, no. Have you reproduced the problem on other machines? – Jon Skeet Aug 01 '11 at 13:59
  • @Jon Skeet: I will try on 2 laptops tonight. – Lazlo Aug 01 '11 at 16:36
  • I could now reproduce the issue, but it's not very obvious. Look at my answer below (it's simply too large of an explanation to fit in a comment). It may help other people figuring this out. – Christopher Currens Aug 01 '11 at 21:36
  • The user's answer (who is also the OP) is asking more questions in his answer. It appears like this answer should be another question in of itself, instead of the 'accepted answer' (or, he should link to this answer (the part that just answers the question), and post the new question as a new question). – George Stocker Jan 10 '12 at 14:40
  • @GeorgeStocker I gave the bounty to the Christopher Currens, who answered the second question and did all the "underlying work". The accepted answer (this one) fixes the initial question, and is as such marked as accepted. – Lazlo Jan 10 '12 at 22:55
  • @LazloBonin Your question that you pose in this answer should not be here. It should be a new question. That was my main beef. – George Stocker Jan 11 '12 at 01:09
3

my guess is that some old compiled code is hanging around somewhere.. especially if the problem went away suddenly

  • Have you moved any type arguments around lately?
  • Are you incrementing assembly versions on build? (can cause problems because the fully qualified names of the types changes)
  • Whats the scenario where this exception happens, is it a client calling a server using diffrent copies of the binaries?

If any of these questions are true i'd remove every binary i could find and rebuild everything from scratch :)

-edit-

Also, make sure you're not accidently referencing binary files directly unless you really really have to. You should always use project references to make sure everything gets rebuilt properly.

-edit2-

Okay this is sooo strange.. i pasted in your code in a playground solution i have, got the exception. but now i tried your compiled version, and it worked!

I diffed the code with my old version, exactly the same...

I diffed the projfiles, not exactly the same, but i copied all the details so that they where, still your project works, mine didnt!

So i checked the solution files.. no diffrent appart from project guids.., still the same situation..

So i removed the only other thing i could think of, the .suo file for my playground solution.. and they the both worked..

The suo files seems to be binary so im not really sure what exactly is set in there. i do know that i had that suo file prior to installing .net/vs2010 sp1 though, maybe there is some old stuff in there, who knows. i'll try and investigate more.

-edit4-

Well i dont know whats going on.. now i cant get the code to crash again. Even copying the old .suo file back doesnt work..

aL3891
  • 6,205
  • 3
  • 33
  • 37
  • I have moved the type arguments, I increment on build, but I don't remotely call from client to server. The fact that there is networking is irrelevant, since the exception occurs before even a socket could be created. I'll see about rebuilding the whole shabang later. – Lazlo Jul 25 '11 at 22:52
  • Moving type arguments is "dangerous" as this will generally not produce compiler errors, but may significantly alter the semantics of the code. This is especially true if you infer the generics at the call site, that is, pass in the objects without explicitly giving the type aruments. Its a very common thing to do though (i almost always do it). My bet is on mismatched assembly version numbers though, you might have rebuilt the base class assembly, but not all the derived ones for example. – aL3891 Jul 25 '11 at 22:58
  • hm, actually it might not be the version numbers, but an old version of what ever assembly contains the type you're trying to create when you get the exception. Try using reflector on that perticular dll and see if its actually using the correct order for the type arguments. (TInPacket and TOutpacket have switched place recently right?) – aL3891 Jul 25 '11 at 23:16
  • Mmmm, trying to reproduce the bug seeming impossible, it might really have to do with the assemblies as you say. Looking into it right now. – Lazlo Jul 27 '11 at 02:53
  • Question updated, cause found. Please read my answer. – Lazlo Jul 27 '11 at 03:09
  • Mm, interesting information about the SUO, but as you noted, after some tries it just becomes irrelevant. It doesn't affect the crashing behaviour. – Lazlo Aug 02 '11 at 00:13
2

If you indeed use no reflection this seems to indicate a bug in the C# compiler or the runtime. Usually this results in unverifiable code.

It seems like you created a construct that the runtime considers illegal, but the C# compiler did not recognize as illegal. Which one has the bug is difficult to say since you omitted the essential type declarations.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262