3

I have written a C# library(Kvp) for VBA which is essentially a wrapper for a C# dictionary. All of the methods I have implemented work fine from VBA. However the indexer is throwing errors when I assign to an item in the Dictionary (the same problem occurs if I change from a dictionary to a HashTable).

In VBA

myObject.Item(Index) = a_value

throws an error saying that an object is required.

If I cast myObject to Object then the indexer works as expected.

Dim myObj as Object
Set myObj = myObject
myObj.Item(Index) = a_value

To confirm that the Indexer was the problem I added GetItem and SetItem methods to the C# library and found that these worked as expected.

In the Interface

dynamic this[dynamic Key] { get; set; }
dynamic GetItem(dynamic myIndex);    
void SetItem(dynamic myIndex, dynamic myValue);

In the Implementation

private Dictionary<dynamic, dynamic> MyKvp = new Dictionary<dynamic, dynamic>();

public dynamic this[dynamic Key]
{    
    get
    {
        return MyKvp[Key];
    }
    set
    {
        MyKvp[Key] = value;
    }

public dynamic GetItem(dynamic myIndex)
    {
        return MyKvp[myIndex];
    }
}

public void SetItem(dynamic myIndex, dynamic myValue)
{
    MyKvp[myIndex] = myValue;
}

I can continue with the cast to Object workaround but to be honest it is a bit of a pain as it means that everywhere I change from Scripting.Dictionary to myObject I have to update the associated code. Of course I could revert to implementing myObject in VBA but I'm trying to learn c# and this library is my first project in the learning curve. Please note that I am not a professional programmer.

An of course the question is. How do I revise the C# library such that the Indexer works as expected in VBA?

Amended 13 Dec

Please note that I have a long list of unit tests using the Rubberduck add in. All tests pass except the Indexer assignment. The Indexer assignment test works if I VBA cast to an object as explained above.

Amedenment 2 13 Dec Updated as Amendment 4

In respone to @Gser'g question I've corrected the image for the Kvp Object and also added an image of the IKvp interface .

In the Interface

[Guid("6DC1808F-81BA-4DE0-9F7C-42EA11621B7E")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IKvp

In the Implementation

[Guid("434C844C-9FA2-4EC6-AB75-45D3013D75BE")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Kvp : IKvp, IEnumerable

Object Browser for Kvp from VBA

enter image description here

enter image description here

enter image description here

Amendment 3 A confession

I have asked the same question in Code Review but its not getting much interest. The whole code for the Kvp object is included at

https://codereview.stackexchange.com/questions/233671/c-dictionary-wrapper-for-vba

freeflow
  • 4,129
  • 3
  • 10
  • 18
  • Did you try following : MyKvp[Key] = (object)value; – jdweng Dec 13 '19 at 11:37
  • Does the VBA code have an OPTION BASE 1 ? https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/option-base-statement – Tony Dec 13 '19 at 11:44
  • @jdweng Thanks for the suggestion. Using the (object) cast in the Indexer does not resolve the issue. – freeflow Dec 13 '19 at 11:48
  • @Tony All the relevant methods for populating a 'myObject' start with a Key of 0 if the Key is integer (that means from VBA an Integer, Long or LongLong). – freeflow Dec 13 '19 at 11:57
  • Using the [Guid] attribute is quite risky. You'll have no protection against the VBA code using a stale type library or loading an old copy of the DLL. A generic problem called "DLL Hell". Which can cause all kind of mishaps when it calls the completely wrong method or property setter. Casting to Object is a workaround for that, it forces the VBA runtime to lookup the property at runtime. – Hans Passant Dec 13 '19 at 15:45
  • @HansPassant Thanks for the observation. I commented out the Guid ID lines and the Library still compiles so that another step up the learning curve. – freeflow Dec 13 '19 at 16:13
  • 1
    I believe it is answered in https://social.msdn.microsoft.com/Forums/vstudio/en-US/66be03a9-27e0-4ca8-b289-6c739543f302/c-class-indexer-access-from-vb6-via-com-interop?forum=clr. – GSerg Dec 13 '19 at 16:39
  • Another view on the same problem: https://stackoverflow.com/a/6453646/11683 – GSerg Dec 13 '19 at 16:42
  • @GSerg Thanks for the links. The second is from before C# implemented indexers but does recommend writing the Interface in VB.NET as this has no problem with indexed properties. But I don't think I'm upto doing the necessary jiggery pokery. I'm happy to have a bash as reimplementing the c# library in VB.NET but it would still be nice to get an interanl C# solution. – freeflow Dec 13 '19 at 17:42
  • No, it is not from before C# implemented indexers. It uses VB to create a parametrized property instead of an indexer. C# still does not have that. – GSerg Dec 13 '19 at 17:45
  • @GSerg Sorry, My confusion. I've now implemented the library in VB.NET but I get the same problem. – freeflow Dec 13 '19 at 22:06

0 Answers0