Why does this generic cast fail - c#

I have the following code:
kAIDataPort<T> lOtherEndCast = ( kAIDataPort<T>)lOtherEnd;
Which raises the following exception:
[A]kAI.Core.kAIDataPort`1[UnityEngine.GameObject] cannot be cast to
[B]kAI.Core.kAIDataPort`1[UnityEngine.GameObject]. Type A originates from 'kAICore...
(Exception shrunk for readability, but there is no difference between A and B.
All the other questions on generic casting seem to relate to lists and inherited types, but here we just have an object of a certain type and not being able to cast it to precisely that type.
Not looking for a work around, I am using a non-generic base class with a non-typed method to do what I need to do, I just want to understand why this raises an exception.
This is in .NET 3.5 (since using Unity which still doesn't support .NET 4...)
Full exception:
[A]kAI.Core.kAIDataPort`1[UnityEngine.GameObject] cannot be cast to
[B]kAI.Core.kAIDataPort`1[UnityEngine.GameObject].
Type A originates from 'kAICore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'E:\dev\C#\kAI\kAI-Editor\bin\Debug\kAICore.dll'.
Type B originates from 'kAICore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'E:\dev\C#\kAI\kAI-Editor\bin\Debug\kAICore.dll'.
Update:
The problem was loading the Unity DLL twice, but it was loading the same DLL (but never unloading). The code for this is:
FileStream lDLLStream = lDllPath.GetFile().OpenRead();
byte[] lDLLArray = new byte[lDLLStream.Length];
lDLLStream.Read(lDLLArray, 0, (int)lDLLStream.Length);
lDLLStream.Close();
Assembly lLoadedAssembly = Assembly.Load(lDLLArray);
// Force the loading of the dll
lLoadedAssembly.GetExportedTypes();
return lLoadedAssembly;
The path was the same both times, so why would the case get confused about which dll to load from?
Further, loading the DLL twice this way and examining the following things just before the exception I get:
this.GetType().Equals(lOtherEnd.GetType()) false
but on the generic argument:
typeof(T).Equals(lOtherEnd.GetType().GenericTypeArguments[0]) true

It's a generic type - the cast checks four types: T1<T2> versus T3<T4>.
So, the collision can be either T1<G> vs T2<G> OR it can be T<G1> vs T<G2>.
The debug messages only printed information about 'T' and indeed it seems the same. So, check the Gs.
EDIT:
Now I would not guess you'd do that. You were precisely generating multiple images of the same assembly. All types created from them would always be different.
An assembly that is loaded FROM BYTES is never 'equal' to any other assembly. It is always loaded with new handle and never "coerced/collapsed" with any already loaded assembly. It is always described as having null codebase/location and treated similarily to "dynamic assemblies", created on-the-fly. ( I tried to find a reference for that and I cannot. I'm still quite sure about it, but I'm striking it just to warn you that it may not be true. Here's a starter for reading about assembly loading contexts: What are 3 kinds of Binding Contexts for? )
Why do you load it from raw bytes? Load it from FILE/PATH. Then it will get its codebase/location set properly and multiple loads will results in only one handle in memory.

It seems you're casting a type T1 from assembly A1 to T1 in assembly A2. Indeed both are not same. So it fails.

Related

How to find out origin of unknown type

I have a C# function that makes a call to an API. This function returns an untyped object because of reasons.
Now, what I want to do is to cast that object to its actual type. I know what the type is called, but that's about it. Specifically, I can't cast it because I do not know which class I have to import.
So, what I would like to do is this:
object drawing = DrawingSoapLoader.GetDrawing(ITEM_NUMBER, REVISION_CODE, DRAWING_TYPE);
if(drawing is CADDrawing){
CADDrawing cadDrawing = (CADDrawing) drawing;
[...]
}
...however, that does not work because the type CADDrawing is not recognized.
Currently, what I have working instead is this:
object drawing = DrawingSoapLoader.GetDrawing(ITEM_NUMBER, REVISION_CODE, DRAWING_TYPE);
Type type = drawing.GetType();
Assert.AreEqual("CADDrawing", type.Name);
By debugging this, I can see that the type is this:
Name = "CADDrawing"
FullName = "CADDrawing"
Assembly = {gd5rgwpt, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null} System.Reflection.Assembly {System.Reflection.RuntimeAssembly}
I've also confirmed that the assembly code (here "gd5rgwpt") is different every time I run this.
I can find that .dll in my \AppData\Local\Temp folder, and I can even see that it has the CADDrawing type inside, and while I suppose I could just copy that .dll and manually add it to my project, somehow I don't think this is the right way to go about this.
Is there a better way to go about this?
Or is this just a case where it would be better to create a local CADDrawing class and then cast it to that?

"As" cast is unexpectedly failing

I'm doing a bunch of weird generic stuff which includes doing a lot of casts of delegate types, and for the most part it's working perfectly.
However, I'm running into cast failure that I can't find a reason for.
Here's my method, and following debug data.
public static T GetInvokableDelegate<T>(string name) where T : class
{
return DelRegistry[name].GetSelectedMethod() as T;
//DelRegistry[name].GetSelectedMethod() returns System.Object,
//which may be any flavor of Func<> or Action<>
}
//These two pairs represent the values from the Debug view,
//of T and GetSelectedMethod() respectively
//This is when DelRegistry is returning the initial result from GetSelectedMethod(): This cast As T works fine.
//System.Action<string,AIDECore.Controller>
//{Method = {Void HAL_TestMethod(System.String, AIDECore.Controller)}}
//This is after the second call, GetSelectedMethod() is returning a different delegate, but with identical signature: This one fails
//System.Action<string,AIDECore.Controller>
//{Method = {Void HAL_TestMethod(System.String, AIDECore.Controller)}}
Inspecting the values at runtime shows that they appear to be identical. The specified cast works the first time, fails the second time.
Of note: it's not the number of times it's been called causing it to fail. In other test cases, I've added dozens of delegate versions to DelRegistry without problems.
My question is, can you get error output as to WHY an "As" cast fails? (Where failing means returning null)
Cast exception data:
[A]System.Action`2[System.String,AIDECore.Controller] cannot be cast to [B]System.Action`2[System.String,AIDECore.Controller].
Type A originates from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' in the context 'LoadNeither' at location 'C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'.
Type B originates from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' in the context 'LoadNeither' at location 'C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'.
So that basically says it can't cast.
For the record, all versions returned from GetSelectedMethod() subsequent to the first, are from dynamically loaded Assemblies.
So why has this casting worked fine hundreds of times until now?
Delegate assignments are based on signature, but casts are not. So if in your second case the T delegate type is different the cast "failing" is expected behavior.
To convert delegate types, use code like this.
Edit: after reading your edit with the cast exception data, my bet is that your code somehow loads multiple different versions of the assembly holding the AIDECore.Controller type - check those types.

Get a type from an unreferenced assembly

I want to get a type from an unreferenced assembly. I tried the suggestion in this question: How to get a type from an unreferenced assembly? like this:
Assembly assembly = Assembly.LoadFrom(#"c:\Path\To\My\Assembly\myAssembly.dll");
Type myType = assembly.GetType("myAssembly.MyClass");
Reading the assembly works, but the GetType method returns null. The requested type (MyClass) is public, so this can't be the problem as a read in one answer on the question which i linked above.
EDIT:
I have to load many different Types from different Assemblies. The type which i want to load is defined in a XML-file. Now the myType.FullName is stored in the XML file and this works. But so I don't get the name of the assembly, where the type is stored. My question is now:
Is it possible to get the type with the GetType-Method by specifying the myType.AssemblyQualifiedName?
I tried this but it didn't work for me - the saved type is null.

Two Types not equal that should be

I'm trying to debug some code that uses reflection to load plugins
Here's the debugging code:
Type a = methodInfo.GetParameters()[0]
.ParameterType.BaseType;
Type b = typeof(MessageContext);
Debug.WriteLine(a.AssemblyQualifiedName);
Debug.WriteLine(b.AssemblyQualifiedName);
Debug.WriteLine(a.Equals(b));
And here is its output:
OrtzIRC.Common.MessageContext, OrtzIRC.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
OrtzIRC.Common.MessageContext, OrtzIRC.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
False
I don't understand what would make these two types different?
The same class / type loaded by different app domains [.NET] or class loaders [Java] will not compare equal and are not assignable to/from each other directly.
You likely have two copies of the DLL containing that type - one loaded by the main program and one loaded by one of the Assembly.Load*(...) methods?
Try displaying / comparing the properties:
a.Assembly.Equals(b.Assembly)
and
a.Assembly.Location.Equals(b.Assembly.Location)
In general, you only want one copy of each DLL and have it loaded into a single app domain.
This can happen if the two types are loaded from different versions of the assembly. .NET considers them different, unrelated types. Check
Debug.WriteLine (a.AssemblyQualifiedName) ;
Debug.WriteLine (b.AssemblyQualifiedName) ;
Try: Debug.Writeline(a.Equals(b));
This is how you should compare the types:
C# Object Type Comparison
I think your problem resides in the type hierarchy...

Dynamically Loading a DLL

I am trying to simply load a dll written in C# at run time and create an instance of a class in that dll.
Assembly a = Assembly.LoadFrom(#"C:\Development\DaDll.dll");
Type type = a.GetType("FileReleaseHandler", true);
TestInterface.INeeedHelp handler = Activator.CreateInstance(type) as TestInterface.INeeedHelp;
No errors are thrown, and if I step through the code I can walk though the FileReleaseHandler Class as it executes the constructor but the value of handler is always null.
What am I missing here? or even is there a better way I should be going about this?
Where is TestInterface.INeedHelp defined? One common problem is if you've got the same interface in multiple assemblies. If both the caller and the dynamically loaded assembly reference the same interface in the same assembly, it should be okay.
One subtlety is that if the assembly is in a different directory to the calling assembly, it may end up loading a different copy of the same assembly, which can be very irritating :(
Try setting the result of Activator.CreateInstance to an object directly, and not casting.
It's possible FileReleaseHandler does not implement TestInterface.INeeedHelp, in which case, this will be set to null via the "as TestInterface.INeeedHelp".
Check the assemblies that are loaded into the application domain. Are there two assemblies with the TestInterface.INeedHelp interface in it? My suspicion is that the LoadFrom is binding this created object to a different TestInterface.INeedHelp than the one to which you are trying to cast. Try doing a normal downcast rather than a so-called "safe" cast and see what error you get.
The problem is that the LoadFrom method loads the dll into "LoadFrom" context which is different from the default context. The rules of type resolution for the types in this context differ from the ones you are used to. It is possible that the INeedHelp interface as defined in your main module from the standpoint of the runtime is different from the interface with the same name as coming from the LoadFrom context. This will cause the cast (as TestINterface) return null.
To see if this is the case try to assign the result to an object.
I would do something like :
Assembly a = Assembly.LoadFrom(#"C:\Development\DaDll.dll");
object obj = a.CreateInstance("fullClassName",other params) as IMyType
//(if it implements an interface you referenced)

Categories

Resources