我对这个问题的调查结果:
问题与公共语言运行时在向COM公开双接口和调度接口时使用的IDispatch实现有关.
像VBScript(ASP)这样的脚本语言在访问COM对象时使用OLE Automation IDispatch实现.
尽管它似乎有效,但我希望将该属性保留为属性,并且不希望具有某个功能(上面解释了解决方法).
您有两种可能的解决方案:
1 - 将弃用的IDispatchImplAttribute与IDispatchImplType.CompatibleImpl一起使用.
[ClassInterface(ClassInterfaceType.None)] [IDispatchImpl(IDispatchImplType.CompatibleImpl)] public class TestRequest : IRequest { private IRequestDictionary _queryString = new RequestDictionary(); public IRequestDictionary QueryString { get { return _queryString; } } }
如MSDN中所述,此属性已弃用但仍可使用.Net 2.0,3.0,3.5,4.0.你必须决定它是否"弃用"这一事实对你来说可能是一个问题......
2 - 或者在类TesRequest中将IReflect实现为自定义IDispatch,或者创建一个实现IReflect的泛型类,并使您的类继承这个新创建的类.
通用类示例(interresting部分在InvokeMember方法中):
[ComVisible(false)] public class CustomDispatch : IReflect { // Called by CLR to get DISPIDs and names for properties PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { return this.GetType().GetProperties(bindingAttr); } // Called by CLR to get DISPIDs and names for fields FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { return this.GetType().GetFields(bindingAttr); } // Called by CLR to get DISPIDs and names for methods MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { return this.GetType().GetMethods(bindingAttr); } // Called by CLR to invoke a member object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) { try { // Test if it is an indexed Property if (name != "Item" && (invokeAttr & BindingFlags.GetProperty) == BindingFlags.GetProperty && args.Length > 0 && this.GetType().GetProperty(name) != null) { object IndexedProperty = this.GetType().InvokeMember(name, invokeAttr, binder, target, null, modifiers, culture, namedParameters); return IndexedProperty.GetType().InvokeMember("Item", invokeAttr, binder, IndexedProperty, args, modifiers, culture, namedParameters); } // default InvokeMember return this.GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); } catch (MissingMemberException ex) { // Well-known HRESULT returned by IDispatch.Invoke: const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); throw new COMException(ex.Message, DISP_E_MEMBERNOTFOUND); } } FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) { return this.GetType().GetField(name, bindingAttr); } MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) { return this.GetType().GetMember(name, bindingAttr); } MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { return this.GetType().GetMembers(bindingAttr); } MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) { return this.GetType().GetMethod(name, bindingAttr); } MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { return this.GetType().GetMethod(name, bindingAttr, binder, types, modifiers); } PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { return this.GetType().GetProperty(name, bindingAttr, binder, returnType, types, modifiers); } PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) { return this.GetType().GetProperty(name, bindingAttr); } Type IReflect.UnderlyingSystemType { get { return this.GetType().UnderlyingSystemType; } } }
并为迈克的代码:
[ClassInterface(ClassInterfaceType.None)] public class TestRequest : CustomDispatch, IRequest { private IRequestDictionary _queryString = new RequestDictionary(); public IRequestDictionary QueryString { get { return _queryString; } } }
几天前,我偶然发现了这个确切的问题。我找不到合理的解释,为什么它不起作用。
在花了很长时间尝试各种解决方法之后,我认为我终于找到了似乎可行的东西,而且还没有那么脏。我所做的是将容器对象中集合的访问器实现为方法而不是属性。此方法接收一个参数,即密钥。如果键为“ missing”或为null,则该方法返回该集合(它处理VbScript中的诸如“ testRequest.QueryString.Count”之类的表达式)。否则,该方法从集合中返回特定项。
这种方法的肮脏之处在于该方法返回一个对象(因为有时返回引用是集合,有时是集合的一项),因此从托管代码使用它需要在各处进行转换。为了避免这种情况,我在暴露集合的容器中创建了另一个属性(这次是一个适当的属性)。此属性不公开给COM。从C#/托管代码中,我使用此属性;从COM / VbScript /非托管代码中,我使用方法。
这是使用此线程的示例的上述解决方法的实现:
[ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IRequest { IRequestDictionary ManagedQueryString { get; } // Property to use form managed code object QueryString(object key); // Property to use from COM or unmanaged code } [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public class TestRequest : IRequest { private IRequestDictionary _queryString = new RequestDictionary(); public IRequestDictionary ManagedQueryString { get { return _queryString; } } public object QueryString(object key) { if (key is System.Reflection.Missing || key == null) return _queryString; else return _queryString[key]; } } [ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IRequestDictionary : IEnumerable { [DispId(0)] object this[object key] { [DispId(0)] get; [DispId(0)] set; } int Count { get; } } [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public class RequestDictionary : IRequestDictionary { private Hashtable _dictionary = new Hashtable(); public object this[object key] { get { return _dictionary[key]; } set { _dictionary[key] = value; } } public int Count { get { return _dictionary.Count; } } #region IEnumerable Members public IEnumerator GetEnumerator() { throw new NotImplementedException(); } #endregion }