Open sherryyshi opened 3 months ago
Hi
Thanks for your question.
Why your function Test in C# doesn't define a double as argument ? By defining an object, the decoder cannot infer the recipient type. In addition it generates boxing allocations which can be harmful in an heavy usage. In your example you trigger a boxing (aka: native value wrapped in an object) You could reproduce this boxing manually by defining your own types. For example:
interface IBoxing { object Value {get; } } class DoubleBox : IBoxing { double Value { get; set; } object IBoxing.Value => Value; } class Int32Box : IBoxing { int Value { get; set; } object IBoxing.Value => Value; }
or more flexible: class ObjectBox : IBoxing { object Value { get; set; } object IBoxing.Value => Value; }
void Test(IBoxing boxing) { _x = boxing?.Value; } ...
Let me know if it helped.
Regards
Here is a simple repro:
C# code:
Python code:
This causes an exception to be thrown here
((Func<PyObject, T>)decoder)(pyObj);
(full stack trace in Details section below).This is because at runtime,
decoder
is of type(Func<PyObject, double>)
and T is typeobject
, and the cast doesn't work.Unfortunately, when this exception is hit, the entire Python process gets killed. There is no way to catch it as a consumer of this package.
Proposed solution One solution is to cast the return type of
decoder
toT
instead. However, this is not trivial because the type ofdecoder
isobject
at compile time, and the compiler would not allow you to call it as a function without doing the type casting that is currently in place. One way to work around this would be to not use the_decoders
dictionary, but to use a switch/case block instead:Details
Full stack trace: ``` Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidCastException: Unable to cast object of type 'System.Func`2[Python.Runtime.PyObject,System.Double]' to type 'System.Func`2[Python.Runtime.PyObject,System.Object]'. at PandasNet.Codecs.TryDecode[T](PyObject pyObj, T& value) in /home/runner/work/pandasnet/pandasnet/dotnet/PandasNet/Codecs.cs:line 54 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr) --- End of inner exception stack trace --- at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr) at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at Python.Runtime.PyObjectConversions.<>c__DisplayClass9_0.g__TryDecode|0(BorrowedReference pyHandle, Object& result) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Codecs/PyObjectConversions.cs:line 109
at Python.Runtime.PyObjectConversions.TryDecode(BorrowedReference pyHandle, BorrowedReference pyType, Type targetType, Object& result) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Codecs/PyObjectConversions.cs:line 87
at Python.Runtime.Converter.ToManagedValue(BorrowedReference value, Type obType, Object& result, Boolean setError) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Converter.cs:line 382
at Python.Runtime.Converter.ToManaged(BorrowedReference value, Type type, Object& result, Boolean setError) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Converter.cs:line 273
at Python.Runtime.MethodBinder.TryConvertArgument(BorrowedReference op, Type parameterType, Object& arg, Boolean& isOut) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/MethodBinder.cs:line 666
at Python.Runtime.MethodBinder.TryConvertArguments(ParameterInfo[] pi, Boolean paramsArray, BorrowedReference args, Int32 pyArgCount, Dictionary`2 kwargDict, ArrayList defaultArgList, Int32& outs) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/MethodBinder.cs:line 629
at Python.Runtime.MethodBinder.Bind(BorrowedReference inst, BorrowedReference args, Dictionary`2 kwargDict, MethodBase[] methods, Boolean matchGenerics) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/MethodBinder.cs:line 407
at Python.Runtime.MethodBinder.Bind(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase info, MethodBase[] methodinfo) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/MethodBinder.cs:line 366
at Python.Runtime.MethodBinder.Invoke(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase info, MethodBase[] methodinfo) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/MethodBinder.cs:line 868
at Python.Runtime.MethodObject.Invoke(BorrowedReference target, BorrowedReference args, BorrowedReference kw, MethodBase info) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Types/MethodObject.cs:line 77
at Python.Runtime.MethodBinding.tp_call(BorrowedReference ob, BorrowedReference args, BorrowedReference kw) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Types/MethodBinding.cs:line 166
at Python.Runtime.Runtime.PyObject_Call(BorrowedReference pointer, BorrowedReference args, BorrowedReference kw) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Runtime.cs:line 919
at Python.Runtime.ManagedType.Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Types/ManagedType.cs:line 136
at Python.Runtime.ClassBase.Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Types/ClassBase.cs:line 634
at Python.Runtime.MetaType.tp_call(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) in /tmp/build-via-sdist-h3j67_wc/pythonnet-3.0.3/src/runtime/Types/MetaType.cs:line 255
```