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.
Related
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?
I am trying to get all the types in an assembly (assembly.GetTypes()) and I try to get a span from that without any heap-allocation which would look like this assembly.GetTypes().AsSpan(). However the weird thing is that this throws the following error:
System.ArrayTypeMismatchException: 'Attempted to access an element as a type incompatible with the array.'
Which kinda confused me, what made it even more confusing is that assembly.GetTypes().ToArray().AsSpan() runs without any issues, but this obviously isn't a solution.
A fiddle, which shows this can be found right here.
assembly.GetTypes() returns a RuntimeType[], but it uses array type covariance to disguise it as a Type[]. Per the docs for the Span constructor, an ArrayTypeMismatchException is thrown if T (which in this case is Type) does not match the runtime array type (which in this case is RuntimeType).
assembly.GetTypes().ToArray() on the other hand returns an actual Type[]: it will create a brand new array based on the declared type T, which is Type, so you get a true Type[] array and there is no mismatch -- T is Type and you're passing a true Type[] to the Span constructor.
ReadOnlySpan<T> does not have this constraint, since it is not possible to write an incompatible Type to a read-only RuntimeType[], so you can do this:
new ReadOnlySpan<Type>(assembly.GetTypes());
I'm in the process of learning Dapper and I'm having trouble creating instances of anonymous types with it. First of all, some context: we have a method that returns an IEnumerable<T>. Note that T can be anonymous.
IEnumerable<T> ExecuteCollection<T>(...)
{
// Query building logic goes here.
var statement = Something.Statement;
var parameters = Something.Parameters;
return _connection.Query<T>(statement, parameters);
}
This code works for registered classes like a charm. However, I want it to handle anonymous types as well, and the problem that is throw by the compiler is:
InvalidOperationException: "A parameterless default constructor or one matching signature ([signature of anonymous object]) is required for <>f__AnonymousType5`2[[System.Int16, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=XXX],[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=XXX]] materialization"
I know that the issue is the fact that anonymous objects have no constructors to work with. An alternative to that would be (T)Activator.CreateInstance(typeof(T), row), but the row variable must be an object[], not a DapperRow.
Whenever I searched for information regarding this topic, everyone said that I shouldn't work with anonymous types to begin with. However, it is required that my function returns a collection of anonymous type items.
This is the first time I'm using Dapper, and I don't know whether I'm misunderstanding things or not. The question is: how can I create an instance of an anonymous object with Dapper, or what is the alternative around this problem so that my method can still return IEnumerable<T>? I'm using .NET Core.
I think, unless I'm misunderstanding you just want to call
connection.Query(statement, parameters);
Without the generic arguments to do exactly what you are asking for to give you a dapper row. Then you can write
connection.Query(statement, parameters).Select(x => new { Something = x.Something});
To create an anonymous object.
Or to get a little more complicated you could convert to an expando object by casting the row to IDictionary<string,object>, and iteratively assigning the properties to an expando object.
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.
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...