如何获取数组的COM对象类型?解决VBA自定义对象数组去重类型异常
目标
通过反射与泛型传入自定义对象的VBA数组,处理后返回相同类型的VBA COM对象(示例功能为对以变体形式传入的任意类型VBA数组去重)。
现状
值类型数组处理符合预期,返回与传入类型一致的数组(参考RemoveDuplicates方法)。
问题
自定义VBA对象数组(如Person数组)处理后,返回的是包含Person对象的变体数组,而非预期的Person数组。
临时方案
使用RemoveDuplicates2方法传入VBA数组和非泛型IEqualityComparer可实现自定义对象数组去重,但无法返回所需的自定义COM对象数组。
备注
目前为避免潜在问题,将Array.cs中的RemoveDuplicates重载命名为唯一名称,后续需研究表达式树相关内容。
SafeArraySingleton.cs
using GSystem = global::System; using GArray = global::System.Array; using GType = global::System.Type; using GMethodInfo = global::System.Reflection.MethodInfo; using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Reflection; using System.Collections; using DotNetLib.System.Collections.Generic; namespace DotNetLib.System { [ComVisible(true)] [Description("Provides methods for creating, manipulating, searching, and sorting safe arrays.")] [Guid("37592E87-83CE-4D47-A510-324FE529BE27")] [ProgId("DotNetLib.System.SafeArraySingleton")] [ClassInterface(ClassInterfaceType.None)] [ComDefaultInterface(typeof(ISafeArraySingleton))] public class SafeArraySingleton : ISafeArraySingleton { const string RemoveDuplicatesMethod = "RemoveDuplicates"; static GType _arrayType = typeof(Array); static GMethodInfo _removeDuplicates = _arrayType.GetMethod("RemoveDuplicates", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); static GMethodInfo _removeDuplicates2 = _arrayType.GetMethod("RemoveDuplicates2", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); static GType _genericComparerType = typeof(GenericEqualityComparer<>); public SafeArraySingleton() { } [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)] public object RemoveDuplicates([MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)][In] ref object inputArray) { if ((inputArray == null) | !(inputArray is GArray)) { throw new ArgumentException("Must be an array.", nameof(inputArray)); } var result = _removeDuplicates.MakeGenericMethod(inputArray.GetType().GetElementType()).Invoke(null, new object[] { inputArray }); return result; } [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)] public object RemoveDuplicates2([MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)][In] ref object inputArray, IEqualityComparer nonGenericComparer) { if ((inputArray == null) | !(inputArray is GSystem.Array)) { throw new ArgumentException("Must be an array.", nameof(inputArray)); } GType elementType = inputArray.GetType().GetElementType(); // 创建非泛型比较器的泛型包装器(通过反射) var genericComparerType = typeof(GenericEqualityComparer<>); var genericType = genericComparerType.MakeGenericType(elementType); var genericComparer = Activator.CreateInstance(genericType, nonGenericComparer); var result = _removeDuplicates2.MakeGenericMethod(elementType).Invoke(null, new object[] { inputArray, genericComparer }); return result; } } }
ISafeArraySingleton.cs
using GCollections = global::System.Collections; using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace DotNetLib.System { [ComVisible(true)] [Guid("5B473306-F5E2-4C83-92F7-CEF4A363E2DD")] [Description("Provides methods for creating, manipulating, searching, and sorting safe arrays.")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface ISafeArraySingleton { [Description("Removes duplicates from an array.")] [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)] object RemoveDuplicates([MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)][In] ref object inputArray); [Description("Removes duplicates from an array.")] [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)] object RemoveDuplicates2([MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)][In] ref object inputArray, GCollections.IEqualityComparer equalityComparer); } }
Array.cs 相关代码
public static T[] RemoveDuplicates<T>(T[] inputArray) { if (inputArray == null) throw new ArgumentNullException(nameof(inputArray)); HashSet<T> set = new HashSet<T>(inputArray); return set.ToArray(); } public static T[] RemoveDuplicates2<T>(T[] inputArray, IEqualityComparer<T> equalityComparer) { if (inputArray == null) throw new ArgumentNullException(nameof(inputArray)); HashSet<T> set = new HashSet<T>(inputArray, equalityComparer); return set.ToArray(); }
GenericEqualityComparer.cs
using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; namespace DotNetLib.System.Collections.Generic { [ComVisible(false)] // 非泛型IEqualityComparer的泛型包装器 // GenericEqualityComparer<T>是一个泛型包装器,将调用委托给非泛型比较器 public class GenericEqualityComparer<T> : IEqualityComparer<T> { private readonly IEqualityComparer originalComparer; public GenericEqualityComparer(IEqualityComparer originalComparer) { this.originalComparer = originalComparer ?? throw new ArgumentNullException(nameof(originalComparer)); } public bool Equals(T x, T y) { return originalComparer.Equals(x, y); } public int GetHashCode(T obj) { return originalComparer.GetHashCode(obj); } } }
内容的提问来源于stack exchange,提问作者M. Johnstone
相关产品推荐
相关产品推荐

