tags:

views:

108

answers:

4

The following code throws an exception. If there is no easy answer or stuff to check, I'll try to produce something that reproduces the error (though I don't know where to upload it).

public static XMLobj Load(string FileName)
{
    if (File.Exists(FileName) == false)
    {
   return null;
    }
    IRDnet.XMLobj def;
    XmlSerializer xmlser = new XmlSerializer(typeof(IRDnet.XMLobj));
    System.IO.Stream stream = File.OpenRead(FileName);
    object o = xmlser.Deserialize(stream);   
        // o appears to be of correct type in the quick watch. 
    IRDnet.XMLobj def2 = o as IRDnet.XMLobj; 
        // def2 is "undefined" (as operator rejected o)
    def = (IRDnet.XMLobj)o;     
        // Throws InvalidCastException with no InnerException.
    stream.Close();
    return def;
}

The strange thing is that "o" appears to be of correct type if I break just before the exception is thrown:

o {IRDnet.XMLobj} System.Object

And the object casts just fine in the quickwatch window. Values are easily inspected.

It is executed from the same project that it is part of. So, no loading contexts. FYI: the static method is part of the XMLobj class if that's relevant. Is there some other criteria for a successful cast that I'm not aware of? Any code that gets implicitly executed?

I've checked that reflector produces the equivalent code to make sure that nothing was lost in compiler optimization.

Any clues, people? I'm stumped. I even hoped that just writing this question would make me think twice on something completely obvious.

+2  A: 

Chances are this is a versioning issue. That is, the deserialized XMLobj is a different version to the one you've compiled with. Check the fully qualified name of each type.

HTH,
Kent

Kent Boogaart
I thought along the same lines. However, I'd expect that to interfere with the Deserialize method, not the subsequent cast. The crux of the matter seem to be the return type of the Deserialize method. As if the "typeof" got ahold of another type than the type declaration in the local scope. Is there any other way to distinguish type than their members and full name? Based on what can a cast fail?
Tormod
+1  A: 

The .NET Serializer produces {Assembly}.Serializer.dll assemblies to speed up XML serialization/deserialization. Try to delete every Assembly and compile from scratch.

If the assemblies does not match exactly an InvalidCast Exception is throw.

EDIT: Look in your debugger output to see what assemblies have been loaded.

Arthur
This sounds highly credible. That some freak assembly was created somewhere that maintain a bastard version of my type. I couldn't find any such assembly in my solution folders or below, however. My project does not register in the GAC, so I see no reason how it could be anywhere but locally. Is the serialize assembly generated at runtime?
Tormod
Ah - yes. They are created during runtime. Normally you don't see them. But you can produce such Assemblies with tools. CLR looks for such a Assembly first before it generates one.
Arthur
+1  A: 

UPDATE:

  string tmp = o.GetType().AssemblyQualifiedName;
  string tmp2 = typeof(XMLobj).AssemblyQualifiedName;

produces:

tmp "IRDnet.XMLobj, IRDnet, Version=1.0.3600.18887, Culture=neutral, PublicKeyToken=null" string tmp2 "IRDnet.XMLobj, IRDnet, Version=1.0.3601.27699, Culture=neutral, PublicKeyToken=null" string

So there is definitely a legitimate type mismatch. I highly appreciate this help. I was completely stuck. Now I have a lead. Now to find how the old type survived the rebuilding.

Maybe the XML file has something to say about it.... Or maybe it is the secretive Serialize-assembly somewhere that was mentioned....

I'm not stuck. I'm still digging, but I thought I'd inform of this new development. If anybody has more tips about interrogating a type about its declaration, then please chip in. Otherwise, thank you all!

Tormod
Look in your debugger output to see from where assemblies have been loaded.
Arthur
or turn on Fusion logging.
Anton Tykhyy
A: 

ANSWER

The first thing to do if you ever get into this exception for seeming identical types, is to check that the two types are actually the same.

System.Type t1 = typeof(XMLobj); 
System.Type t2 = o.GetType();

And then check them in the debugger. Take especially note of the "AssemblyQualifiedName". My two types turned out to be different versions of the same class.

Then do pay careful attention to the output window in Visual Studio as you are single stepping through. It will tell you when which assemblies are loaded. In my example, the culprit came in clear text in the debug output just as I stepped over the line

XmlSerializer xmlser = new XmlSerializer(typeof(IRDnet.XMLobj));

There, the debugger revealed that another, older, version of my assembly was loaded from a location that I wasn't aware of.

I neglected to tell the guys here that I debug using an external exe that loads my assembly. It turns out that the CLR, I assume on behalf of the XmlSerializer class constructor to resolve the "typeof", looks in the exe folder and its subfolders (which is what CLR does). And, lo and behold, deep down there there was a bastard of the original version of the IRDnet.dll file that I was fixing.

I still do not see how the two "typeof" statements produce different results: One results in the object type currently within context, whereas the other causes the CLR do go out looking for it in the execution folder. One possible theory is that the typeof statement is somehow lazily evalutated. I wouldn't know.

Tormod