RuntimeBinderException 通过动态将 0 以外的枚举值传递给 .Net Com 程序集
RuntimeBinderException passing Enum values other then 0 to a .Net Com Assembly via dynamic
我有第一个 C# 程序集公开了一个 class 和一个枚举
namespace TestComLibraryInCSharp
{
[Guid("A5AE3B4C-7788-4394-A949-98A9F78A8E00")]
[ComVisible(true)]
public enum ColorType
{
Red = 0,
Green = 1,
Blue = 2
}
[Guid("EF5D9F9C-DAAB-472E-A418-114F0352F06E")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IComClassUsingEnum
{
void Select(ColorType color);
}
[Guid("5EDE0D14-3A3B-41E7-93BC-40868BC68655")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class ComClassUsingEnum : IComClassUsingEnum
{
public void Select(ColorType color)
{
MessageBox.Show("you have selected " + color.ToString());
}
}
}
在另一个程序集中,我想使用动态测试上述接口
public void DoTestColor()
{
Type type = Type.GetTypeFromProgID("TestComLibraryInCSharp.ComClassUsingEnum");
dynamic c = Activator.CreateInstance(type);
c.Select(0); // this works
c.Select(1); // this throws RuntimeBinderException
}
c.Select(0) 显示消息框
c.Select(1)(或除 0 以外的任何其他数字)将生成此异常
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll
Additional information: The best overloaded method match for 'TestComLibraryInCSharp.ComClassUsingEnum.Select(TestComLibraryInCSharp.ColorType)' has some invalid arguments
在vba,后期绑定,一切正常
Dim c As Object
Set c = CreateObject("TestComLibraryInCSharp.ComClassUsingEnum")
c.Select 1
在 C# 中使用后期绑定传递枚举值的正确方法是什么?
这是一个很好的例子,它表明在 .NET 中编写 COM 服务器测试代码实际上根本不会测试 COM 互操作性。为什么 IDE 反对添加对类型库的引用是一个非常合理的投诉。 CLR 并没有那么容易被愚弄,它可以在运行时看到您创建了一个 .NET 对象,它不会为其创建 RCW。不为难你,只为效率更高
异常是完全正常的。 DLR 遵循 C# 语言规则,只有默认值(0)可以隐式转换为枚举类型。它坚持要看到 ColorType 否则,您将在 C# 程序中使用强制转换。在后期绑定场景中很难做到这一点,您不能为枚举类型提供 ProgId。需要某种偷偷摸摸的后门反射代码,这远远超出了理智测试中的合理范围。
鉴于您根本实际上 测试 COM 互操作性,您不妨解决这个问题并添加一个普通的 .NET 引用。现在很简单了。只有 VBA 测试代码执行 COM 互操作管道。
尽管 Hans 是对的,但解决方案可能是这样
public ColorType GetColorTypeFromInt(int colorTypeAsInt)
{
return (ColorType) colorTypeAsInt;
}
也就是说,在 COM 库中,公开一些从 int 转换为正确枚举类型的辅助方法(这可能就是 Hans 所说的 'sneaky backdoor')。
非常丑陋,但回答了我原来的问题。
我有第一个 C# 程序集公开了一个 class 和一个枚举
namespace TestComLibraryInCSharp
{
[Guid("A5AE3B4C-7788-4394-A949-98A9F78A8E00")]
[ComVisible(true)]
public enum ColorType
{
Red = 0,
Green = 1,
Blue = 2
}
[Guid("EF5D9F9C-DAAB-472E-A418-114F0352F06E")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IComClassUsingEnum
{
void Select(ColorType color);
}
[Guid("5EDE0D14-3A3B-41E7-93BC-40868BC68655")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class ComClassUsingEnum : IComClassUsingEnum
{
public void Select(ColorType color)
{
MessageBox.Show("you have selected " + color.ToString());
}
}
}
在另一个程序集中,我想使用动态测试上述接口
public void DoTestColor()
{
Type type = Type.GetTypeFromProgID("TestComLibraryInCSharp.ComClassUsingEnum");
dynamic c = Activator.CreateInstance(type);
c.Select(0); // this works
c.Select(1); // this throws RuntimeBinderException
}
c.Select(0) 显示消息框
c.Select(1)(或除 0 以外的任何其他数字)将生成此异常
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll
Additional information: The best overloaded method match for 'TestComLibraryInCSharp.ComClassUsingEnum.Select(TestComLibraryInCSharp.ColorType)' has some invalid arguments
在vba,后期绑定,一切正常
Dim c As Object
Set c = CreateObject("TestComLibraryInCSharp.ComClassUsingEnum")
c.Select 1
在 C# 中使用后期绑定传递枚举值的正确方法是什么?
这是一个很好的例子,它表明在 .NET 中编写 COM 服务器测试代码实际上根本不会测试 COM 互操作性。为什么 IDE 反对添加对类型库的引用是一个非常合理的投诉。 CLR 并没有那么容易被愚弄,它可以在运行时看到您创建了一个 .NET 对象,它不会为其创建 RCW。不为难你,只为效率更高
异常是完全正常的。 DLR 遵循 C# 语言规则,只有默认值(0)可以隐式转换为枚举类型。它坚持要看到 ColorType 否则,您将在 C# 程序中使用强制转换。在后期绑定场景中很难做到这一点,您不能为枚举类型提供 ProgId。需要某种偷偷摸摸的后门反射代码,这远远超出了理智测试中的合理范围。
鉴于您根本实际上 测试 COM 互操作性,您不妨解决这个问题并添加一个普通的 .NET 引用。现在很简单了。只有 VBA 测试代码执行 COM 互操作管道。
尽管 Hans 是对的,但解决方案可能是这样
public ColorType GetColorTypeFromInt(int colorTypeAsInt)
{
return (ColorType) colorTypeAsInt;
}
也就是说,在 COM 库中,公开一些从 int 转换为正确枚举类型的辅助方法(这可能就是 Hans 所说的 'sneaky backdoor')。
非常丑陋,但回答了我原来的问题。