I am trying to use a .NET dll in Python. In a .NET language the method requires passing it 2 arrays by reference which it then modifies:
public void GetItems
PythonNet doesn't document this quite as clearly as IronPython, but it does almost the same thing.
So, let's look at the IronPython documentation for ref and out parameters:
The Python language passes all arguments by-value. There is no syntax to indicate that an argument should be passed by-reference like there is in .NET languages like C# and VB.NET via the ref and out keywords. IronPython supports two ways of passing ref or out arguments to a method, an implicit way and an explicit way.
In the implicit way, an argument is passed normally to the method call, and its (potentially) updated value is returned from the method call along with the normal return value (if any). This composes well with the Python feature of multiple return values…
In the explicit way, you can pass an instance of
clr.Reference[T]
for the ref or out argument, and its Value field will get set by the call. The explicit way is useful if there are multiple overloads with ref parameters…
There are examples for both. But to tailor it to your specific case:
itemIDs, itemNames = GetItems()
Or, if you really want:
itemIDsRef = clr.Reference[Array[int]]()
itemNamesRef = clr.Reference[Array[String]]()
GetItems(itemIDs, itemNames)
itemIDs, itemNames = itemIDsRef.Value, itemNamesRef.Value
CPython using PythonNet does basically the same thing. The easy way to do out
parameters is to not pass them and accept them as extra return values, and for ref
parameters to pass the input values as arguments and accept the output values as extra return values. Just like IronPython's implicit solution. (Except that a void
function with ref
or out
parameters always returns None
before the ref
or out
arguments, even if it wouldn't in IronPython.) You can figure it out pretty easily by inspecting the return values. So, in your case:
_, itemIDs, itemNames = GetItems()
Meanwhile, the fact that these happen to be arrays doesn't make things any harder. As the docs explain, PythonNet provides the iterable interface for all IEnumerable
collections, and the sequence protocol as well for Array
. So, you can do this:
for itemID, itemName in zip(itemIDs, itemNames):
print itemID, itemName
And the Int32
and String
objects will be converted to native int
/long
and str
/unicode
objects just as if they were returned directly.
If you really want to explicitly convert these to native values, you can. map
or a list comprehension will give you a Python list from any iterable, including a PythonNet wrapper around an Array
or other IEnumerable
. And you can explicitly make a long
or unicode
out of an Int32
or String
if you need to. So:
itemIDs = map(int, itemIDs)
itemNames = map(unicode, itemNames)
But I don't see much advantage to doing this, unless you need to, e.g., pre-check all the values before using any of them.
I have managed to use the method bool XferData(ref byte[] buf, ref int len) from C# library CyUSB.dll with the following code:
>>> xferLen = 2;
>>> outData=[10, 0]
>>> inData=[]
>>> n, outData, xferLen = XferData(outData, xferLen)
>>> print n, outData[0], outData[1], xferLen
True 10 0 2
Hope this helps someone.