Невозможно назначить элемент Hashtable в классе C #, полученный из VBA - PullRequest
3 голосов
/ 26 октября 2019

Я написал класс C # (KvpHash) для использования в VBA, который предоставляет дополнительные полезные функции для Hashtable. В моем коде VBA у меня есть обширный набор тестов Rubberduck для класса KvpHash, который показывает все функции класса, как и ожидалось, за исключением того факта, что я не могу изменить значение элемента.

От VBA я получаю сообщение об ошибке

424 'Требуется объект'

В классе C # код интерфейса

dynamic this[dynamic Key] { get; set; }

и реализация

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

, где MyKvpHash определен как

private Hashtable MyKvpHash = new Hashtable();

Если я добавлю ссылку mscorelib на VBA, я смогу создать Hashtable непосредственно в VBA, где он полностьюможно изменить значение элемента в хэш-таблице.

Буду признателен за указатели на то, что я делаю неправильно в коде C #, который вызывает ошибку требуемого объекта.

Отредактировано:добавить пример кода VBA

Использование собственного HashTable

Public Sub TestHashtable()
' requires reference to mscorlib.dll

Dim myHt As Hashtable

    Set myHt = New Hashtable
    myHt.Add 5, "Hello5"
    myHt.Add 10, "Hello10"

    Debug.Print myHt.Item(10)
    Debug.Print myHt.Item(5)

    ' the next line works as expected
    myHt.Item(10) = "A new world"
    Debug.Print myHt.Item(10)

End Sub

Дает вывод

Hello10
Hello5
A new world

Использование моего класса KvpHash (оболочки для HashTable)

Public Sub TestKvpHash()

Dim myHt As VBAExtensions.KvpHash
' KvpHash is a C# wrapper for a System.Collections.HashTable

    Set myHt = New KvpHash
    myHt.AddByKey 5, "Hello5"
    myHt.AddByKey 10, "Hello10"

    Debug.Print myHt.Item(10)
    Debug.Print myHt.Item(5)

    ' The next line produces error 424
    myHt.Item(10) = "A new world"
    Debug.Print myHt.Item(10)

End Sub

Дает вывод

Hello10
Hello5

, а затем останавливается с ошибкой 424.

Отредактировано для добавления полного кода C # по запросу.

Кажется, что естьнет файлового хостинга, и у меня нет другого способа предоставить ссылку, поэтому я вставил соответствующий код нижеш. Код изначально был основан на словаре, но я обновил его до Hashtable, когда впервые обнаружил, что не могу назначить элемент. Этот переключатель не изменил поведение моего кода. Пожалуйста, имейте в виду, что я не профессиональный программист, и что предоставленный код, по сути, мой первый набег на C #. Обычно я пишу макросы Word VBA для собственного потребления.

// VBAExtensions
//
// C# Library module for VBA

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;


namespace VBAExtensions
{
    /// <summary>
    /// Enum for accessing the kvphash structure returned by Method Cohorts
    /// </summary>

    public enum CohortType 
    {
        /// <summary>1 = the keys in A plus keys in B that are not shared</summary>
        KeysInAAndOnlyB = 1,
        /// <summary>2 = the Keys from B in A where B has a different value to A</summary>
        KeysInAandBWithDifferentValues,
        /// <summary>3 = the keys that are only in A and only in B</summary>
        KeysNotInAandB,
        /// <summary>4 = the keys that are inA and  B </summary>
        KeysInAandB,
        /// <summary>5 = the keys in A only   </summary>
        KeysInAOnly,
        /// <summary>6 = the keys in B only</summary>
        KeysInBOnly
    }

/// <summary>
    /// KvpHash is a C# class for VBA which implements a Key/Value HashTable
    /// The object is a morer flexible version of the Scripting.Dictionary
    /// </summary>
    [Guid("30F9294B-11B4-4D91-9D7C-7FF02ADB3F11")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IKvpHash
    {


        /// <summary>
        /// Returns/Sets the "Value" specified by "Key" (i) of a Key/Value Pair
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Type used in Set statement (C# dynamic)</returns> 
        dynamic this[dynamic Key] { get; set; }

        /// <summary>
        /// Adds "Value" to the KvpHash using an integer (VBA Long) Key.
        /// The integer key is based on the first available integer greater than or
        /// equal to the Count of the KvpHash
        /// </summary>
        /// <param name="Value"></param>
        void AddByIndex(dynamic Value);

        /// <summary>
        /// Populates this KvpHash using AddByIndex for each character in the string
        /// </summary>
        /// <param name="this_string"></param>
        void AddByIndexAsChars(string this_string);

        /// <summary>
        /// Pupulates this KvpHash using AddByIndex for each substring in this_string delineated by this_seperator
        /// </summary>
        /// <param name="this_string"></param>
        /// <param name="this_seperator"></param>
        void AddByIndexAsSubStr(string this_string, string this_seperator = ",");

        /// <summary>
        /// Pupulates a KvpHash using AddByIndex for each array item
        /// </summary>
        /// <param name="this_array"></param>
        void AddByIndexFromArray(dynamic this_array);

        /// <summary>
        /// Adds "Value" to the KvpHash with a key pf "Key"
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        void AddByKey(dynamic Key, dynamic Value);

        /// <summary>
        /// Groups the keys of the two KvpHash
        /// </summary>
        /// <param name="ArgKvpHash"></param>
        /// <returns>An array of 6 KvpHash
        /// keys in a {1,2,3,4,5,6}
        /// keys in b {1,2,3,6,7,8}
        /// 1 = the keys in A plus keys in B that are not shared            {1,2,3( from A),4,5,6,7,8}
        /// 2 = the Keys from B in A where B has a different value to A     {3( from B) if value is different}
        /// 3 = the keys that are only in A and only in B                   {4,5,7,8}
        /// 4 = the keys that are in A and  B                               {1,2,3,6}
        /// 5 = the keys in A only                                          {4,5}
        /// 6 = the keys in B only                                          {7,8}
        /// </returns>
        KvpHash Cohorts(KvpHash ArgKvpHash);

        /// <summary>
        /// The number of key/vaue pairs in the KvpHash
        /// </summary>
        /// <returns>Long</returns>
        int Count();

        ///// <summary>
        ///// Return the IEnumerator interface for KvpHash
        ///// </summary>
        ///// <returns>IEnumerator</returns>
        //IEnumerator GetEnumerator();

        /// <summary>
        /// Gets the "Key" for the first ocurrence of "Value" in the KvpHash.
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Key</returns>
        dynamic GetKey(dynamic Value);

        /// <summary>
        /// Returns a variant array of the Keys of the KvpHash
        /// </summary>
        /// /// <returns>Variant Array</returns>
        dynamic[] GetKeys();

        /// <summary>
        /// Returns a variant array of the values of the KvpHash
        /// </summary>
        /// <returns>Variant Array</returns>
        dynamic[] GetValues();

        /// <summary>
        /// True if the "Key" exists in the keys of the KvpHash
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Boolean</returns>
        bool HoldsKey(dynamic Key);

        /// <summary>
        /// True if the "Value" exists in the values of the KvpHash
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Boolean</returns>
        bool HoldsValue(dynamic Value);

        /// <summary>
        /// True if the KvpHash holds 0 key/value pairs
        /// </summary>
        /// <returns>Boolean</returns>
        bool IsEmpty();

        /// <summary>
        /// True if the KvpHash holds one or more key/value pairs
        /// </summary>
        /// <returns>Boolean</returns>
        bool IsNotEmpty();

        /// <summary>
        /// True is the "Key" is not found in the keys of the KvpHash
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Boolean</returns>
        bool LacksKey(dynamic Key);

        /// <summary>
        /// True if the "Value" is not found in the values of the KvpHash
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Boolean</returns>
        bool LacksValue(dynamic Value);

        /// <summary>
        /// Reverses the Key/Value pairs in a KvpHash
        /// </summary>
        /// <returns>New KvpHash where: 
        ///     KvpHash.Value(1) = KvpHash Unique values as Value/Key pairs 
        ///     KvpHash.Value(2) = KvpHash Non unique values as Key/Value pairs</returns>
        KvpHash Mirror();

        /// <summary>
        /// Removes the Key/Value pair spacified by "Key" from the KvpHash
        /// </summary>
        /// <param name="Key"></param>
        void Remove(dynamic Key);

        /// <summary>
        /// Removes all Key/Value pairs from the KvpHash
        /// </summary>
        void RemoveAll();

        /// <summary>
        /// Returns true if the Values in KvpHash are unique.
        /// </summary>
        /// <returns>Boolean</returns>
        bool ValuesAreUnique();

        /// <summary>
        /// Returns true if the Values in KvpHash are not unique.
        /// </summary>
        /// <returns>Boolean</returns>
        bool ValuesAreNotUnique();
    }

    [Guid("87E5A539-FDB3-40D0-9CCD-C817F9893C08")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class KvpHash : IKvpHash, IEnumerable
    {

        private Hashtable MyKvpHash = new Hashtable();

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

        public void AddByIndex(dynamic Value)
        {
            int my_index = MyKvpHash.Count + 1;
            while (MyKvpHash.ContainsKey(my_index))
            {
                my_index++;
            }
            MyKvpHash.Add(my_index, Value);
        }

        public void AddByIndexAsChars(string this_string)
        {
            int my_index = MyKvpHash.Count + 1;
            while (MyKvpHash.ContainsKey(my_index))
            {
                my_index++;
            }
            char[] MyArray = this_string.ToCharArray();
            MyKvpHash.Clear();
            for (int i = 0; i <= MyArray.GetUpperBound(0); i++)
            {
                //KvpHash uses ordinal indexes
                MyKvpHash.Add(i + 1, MyArray[i].ToString());
            }
        }

        public void AddByIndexAsSubStr(string this_string, string this_seperator = ",")
        {
            int my_index = MyKvpHash.Count + 1;
            while (MyKvpHash.ContainsKey(my_index))
            {
                my_index++;
            }
            string[] MyArray = this_string.Split(this_seperator.ToArray());
            for (int i = 0; i <= MyArray.GetUpperBound(0); i++)
            {
                //KvpHash uses ordinal indexes
                MyKvpHash.Add(i + 1, MyArray[i]);
            }
        }

        public void AddByIndexFromArray(dynamic this_array)
        {
            int my_index = MyKvpHash.Count + 1;
            while (MyKvpHash.ContainsKey(my_index))
            {
                my_index++;
            }
            for (int i = 0; i <= this_array.GetUpperBound(0); i++)
            {
                //KvpHash uses ordinal indexes
                MyKvpHash.Add(i + 1, this_array[i]);
            }
        }

        public void AddByKey(dynamic Key, dynamic Value)
        {
            MyKvpHash.Add(Key, Value);
        }

        public KvpHash Cohorts(KvpHash ArgKvpHash)
        {
            KvpHash ResultKvpHash = new KvpHash();
            // VBA reports object not set error if the resuly KvpHash are not newed
            for (int i = 1; i < 7; i++)
            {
                ResultKvpHash.AddByKey(i, new KvpHash());
            }

            foreach (DictionaryEntry MyKvpHashPair in MyKvpHash)
            {
                // A plus unique in B
                ResultKvpHash[1].AddByKey(MyKvpHashPair.Key, MyKvpHashPair.Value);

                if (ArgKvpHash.LacksKey(MyKvpHashPair.Key))  // problem is here
                {
                    // In A only or in B only
                    ResultKvpHash[3].AddByKey(MyKvpHashPair.Key, MyKvpHashPair.Value);
                    // In A only
                    ResultKvpHash[5].AddByKey(MyKvpHashPair.Key, MyKvpHashPair.Value);
                }
                else
                {
                    // In A and In B
                    ResultKvpHash[4].AddByKey(MyKvpHashPair.Key, MyKvpHashPair.Value);
                }
            }

            foreach (dynamic MyKey in ArgKvpHash.GetKeys())
            {
                // B in A with different value
                if (ResultKvpHash[1].LacksKey(MyKey))  // Result 0 will contain all of A
                {
                    ResultKvpHash[1].AddByKey(MyKey, ArgKvpHash[MyKey]);
                    ResultKvpHash[3].AddByKey(MyKey, ArgKvpHash[MyKey]);
                    ResultKvpHash[6].AddByKey(MyKey, ArgKvpHash[MyKey]);
                }
                else
                {
                    if (ResultKvpHash[1][MyKey] != ArgKvpHash[MyKey])
                    {
                        ResultKvpHash[2].AddByKey(MyKey, ArgKvpHash[MyKey]);
                    }
                }
            }
            return ResultKvpHash;
        }

        public Int32 Count()
        {
            return MyKvpHash.Count;
        }

        public bool IsEmpty()
        {
            return MyKvpHash.Count == 0;
        }

        public bool IsNotEmpty()
        {
            return !IsEmpty();
        }

        public IEnumerator GetEnumerator()
        {
            foreach (DictionaryEntry my_pair in MyKvpHash)
            {
                yield return my_pair.Value;
            }
        }

        public dynamic GetKey(dynamic Value)
        {
            return this.Mirror()[1][Value];
        }

        public dynamic[] GetKeys()

        {
            return (dynamic[]) MyKvpHash.Keys;
        }

        public dynamic[] GetValues()
        {
            return (dynamic[]) MyKvpHash.Values;
        }

        public bool HoldsKey(dynamic Key)
        {
            return MyKvpHash.ContainsKey(Key);
        }

        public bool HoldsValue(dynamic Value)
        {
            return MyKvpHash.ContainsValue(Value);
        }

        public bool LacksKey(dynamic Key)
        {
            return !HoldsKey(Key);
        }

        public bool LacksValue(dynamic Value)
        {
            return !HoldsValue(Value);
        }

        public KvpHash Mirror()
        {
            KvpHash MyResult = new KvpHash();
            MyResult.AddByIndex(new KvpHash());
            MyResult.AddByIndex(new KvpHash());
            foreach (DictionaryEntry my_pair in MyKvpHash)
            {
                if (MyResult[1].LacksKey(my_pair.Value))
                {
                    MyResult[1].AddByKey(my_pair.Value, my_pair.Key);
                }
                else
                {
                    MyResult[2].AddByKey(my_pair.Key, my_pair.Value);
                }
            }
            return MyResult;
        }

        public void Remove(dynamic Key)
        {
            MyKvpHash.Remove(Key);
        }


        public void RemoveAll()
        {
            MyKvpHash.Clear();
        }

        public bool ValuesAreUnique()
        {
            return MyKvpHash.Count == ((dynamic[]) MyKvpHash.Values).Distinct().Count();
        }

        public bool ValuesAreNotUnique()
        {
            return !ValuesAreUnique();
        }
    }    
}   

1 Ответ

2 голосов
/ 26 октября 2019

@ Freeflow, если вы измените в модуле Word определение myHt, оно будет OK и будет работать нормально.

Public Sub TestKvpHash()

Dim myHt As Object
' KvpHash is a C# wrapper for a System.Collections.HashTable

    Set myHt = New VBAExtensions.KvpHash
    ' Rest of code
...