What is the exact equivalent to "KeyValuePair[] dataArray" (C#) in VB?

Viewed 66

I need to call a C++ routine with a KeyValuePair parameter. I have an example in C#;

 KeyValuePair[] dataArray =
                new KeyValuePair[]{                
                new KeyValuePair() { key = 1234, value = "Atlanta" },  
                new KeyValuePair() { key = 70, value = "Atlanta" },
                new KeyValuePair() { key = 2320, value = "999999999" }};

This seems to translates to;

Dim dataArray As KeyValuePair() = New KeyValuePair() {New KeyValuePair() With {
    .key = 1234,
    .value = "Atlanta"
}, New KeyValuePair() With {
    .key = 70,
    .value = "Atlanta"
}}

in VB.

I have two questions;

What is this structure called? I would call it an array of KeyValuePairs. So I know how to reference the structure when I search.

How does one add additional values dynamically?

EDIT:

More specifically I have a function whose purpose is to build the same type of structure statically built in the C# code above by reading values from a database. I had originally written the code assuming the "list of" key value pairs is what I needed. Here is that code;

Private Function buildDataRecord(ByRef objRecord As OracleDataReader) As List(Of KeyValuePair(Of Integer, String))
    Dim i As Integer = 0
    Dim lstFieldData As List(Of KeyValuePair(Of Integer, String)) = New List(Of KeyValuePair(Of Integer, String))
    Dim strFieldName As String

    On Error GoTo buildDataRecordError
    For i = 0 To objRecord.FieldCount - 1
        strFieldName = objRecord.GetName(i)
        If strFieldName.Substring(0, 3) = "IN_" _
        Then
            lstFieldData.Add(New KeyValuePair(Of Integer, String)(CInt(strFieldName.Substring(3)), Trim(objRecord(i).ToString)))
        End If
    Next
    Return lstFieldData
buildDataRecordError:
            Err.Raise(Err.Number,
                "buildDataRecord",
                Err.Description,
                Err.HelpFile,
                Err.HelpContext)

When calling C++ I get the error;

Cannot marshal 'parameter #4': Generic types cannot be marshaled.

My assumption is I do not have the correct data type.

2 Answers

What is this structure called?

An Array of KeyValuePair. Arrays are CLR types shared by all languages.

How does one add additional values dynamically?

An array is a fixed-size data structure, so you don't just add additional items like you can with a List. But you can resize an array. VB has ReDim, or you can use the generic Array.Resize method that could also be used from C#.

C++ code rely a lot about how your memory is organized for your type. C# generic type are just describing what the type should contains but nothing about memory organisation. Memory organisation is decided by JIT when it knows the true types of T1 and T2. JIT will probably align Item1 and Item2 on multiple of 8 in memory. It can store Item2 before Item1 (for example if item1 is byte and item2 an int). And in Debug the organisation can be different than in Release. This is why generic are not supported.

You have to define your own struct type for all not generic type versions of KeyValuePair you want to use. And I recommend you to use attribute like this to make thing explicit. Example if you want to handle KeyValuePair<byte, int>

[StructLayout(LayoutKind.Explicit)]
public struct KeyValuePairOfIntAndByte // For KeyValuePair<int, byte>
{
    // note how fields are inversed in memory... 
    // this is the kind of optimisation JIT usually does behind your back 
    [FieldOffset(0)]           public int  Item2;
    [FieldOffset(sizeof(int))] public byte Item1;
}

So make conversion of your KeyValuePair<int, byte> instance to an array of this struct and that should be ok.

Note that you will have to pin memory while your C++ code is using this C# array too... otherwise Garbage Collector can decide at any time to move your array elsewhere in memory (while your C++ code is using it...) I strongly recommend you to read this: https://docs.microsoft.com/en-us/dotnet/framework/interop/copying-and-pinning if you want to continue on this way

For information Swig is a library that generate C# code for you that wrap C++ library so you can use C++ library as if it was written in C# (a lot simpler then...) without thinking too much about all the work you have to do. If you look at the generated code you will understand how complex it can be to do interop code right.

Related