在 C# 4 中使用动态类型访问 javascript 对象的属性

Posted

技术标签:

【中文标题】在 C# 4 中使用动态类型访问 javascript 对象的属性【英文标题】:Accessing properties of javascript objects using type dynamic in C# 4 【发布时间】:2011-07-13 19:00:10 【问题描述】:

我在 c# 中将 com 对象定义为动态类型,我可以很容易地调用方法。 但是,当我尝试访问同一对象上的属性时,我得到一个无效的强制转换异常。

有问题的对象是一个数组,从 javascript 传递给托管代码,我希望将它的长度属性作为 int 获取。

我知道我遗漏了一些奇怪的东西,因为我没有收到“不包含定义”异常,我可以使用反射/InvokeMember 轻松访问该属性。

为什么我不能将动态类型的长度属性转换为int?

例如

这失败了

   dynamic com = comObject;
   int i = com.length; // RTBE here.

这可行

   Type type = comObject.GetType();
   int i = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);

* 更新 *

经过大量测试后,我将这种奇怪的情况缩小到多维数组的情况。 所讨论的 com 对象是从 html 文档传递到托管代码的参数。出于所有意图和目的,该对象有时在 JavaScript 中看起来像这样。

var x = ["a1", "a2", "a3"];

当这样的数组出现在托管代码中时,我可以使用动态类型获得长度 AOK。 (即这里第一个失败的例子实际上是有效的)。但是,如果是多维数组比如JavaScript中的如下结构。

var y = [["b1", "b2", "b3"], "a2", "a3"];

然后在尝试动态访问其长度属性时出现错误。请注意,在这种情况下,我仍然可以通过反射访问长度。在我看来,由于某种原因,当多维数组用作动态类型时,长度属性没有得到正确映射......

在我的情况下,我已经解决了(!?)这是在传递它之前向数组添加一个“length_”属性。

var y = [["b1", "b2", "b3"], "a2", "a3"];
y.length_ = y.length;

现在在托管代码中,我可以按预期访问此属性而不会出错。远非理想,但似乎工作......

   dynamic com = comObject;
   int i = com.length_; // 3!

进一步更新

好的,所以对象索引的长度属性似乎也丢失了动态类型。再次,它可以通过反射访问...

失败

   dynamic com = comObject; // js array i.e. var x = [1, 2];
   int i = com[0]; // MissingMemberException - Error while invoking [PROPERTYGET, DISPID(0)].
   int i = com["0"]; // MissingMemberException - Error while invoking [PROPERTYGET, DISPID(0)].

作品

   Type type = comObject.GetType();
   int i = (int)type.InvokeMember("0", BindingFlags.GetProperty, null, comObject, null); // 1

【问题讨论】:

现实生活中的int com.property是什么? 是 javascript 对象的长度属性。 ..我试过 int i = Convert.ToInt32(com.property);还有…… 你试过 var i = com.property 吗? @Taylor - 是的,相同的 ICE 使用 var 【参考方案1】:

简单来说,你不能通过类型 dynamic 在 c# 中访问多维数组的长度属性,除非你似乎首先在 JavaScript 中使用了长度属性...

下面的简单测试非常清楚地表明了这一点。我希望这可以避免其他人在过去一天左右的时间里一直在挠头。

[ComVisibleAttribute(true)]
public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
        webBrowser1.ObjectForScripting = this;

        StringBuilder html = new StringBuilder();
        html.Append("<script>");
        html.Append("var arr1 = [1, 2, 3, 4];");
        html.Append("var arr2 = [arr1, 2, 3, 4];");
        html.Append("var fn1 = function()  return arr1; ;");
        html.Append("var fn2 = function()  return arr2; ;");
        html.Append("var fn3 = function()  alert(arr2.length); ");
        html.Append("</script>");
        webBrowser1.DocumentText = html.ToString();

        webBrowser1.DocumentCompleted += (o, e) =>
        
            dynamic arr1 = webBrowser1.Document.InvokeScript("fn1");
            int i = arr1.length;
            MessageBox.Show(i.ToString()); //4

            // If I call fn3 here then the arr2.length *is* available as int i2 below!
            ////webBrowser1.Document.InvokeScript("fn3"); // 4 

            dynamic arr2 = webBrowser1.Document.InvokeScript("fn2");
            int i2 = arr2.length;
            MessageBox.Show(i2.ToString()); // unless fn3 is called you get...
            /* 
            System.MissingMemberException was unhandled by user code
            Message=Error while invoking length.
            Source=System.Dynamic
            StackTrace:
            at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
            at CallSite.Target(Closure , CallSite , ComObject )
            at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
            at CallSite.Target(Closure , CallSite , Object )
            at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
            */             
        ;
    

更新

如果 WebBrowser 控件使用版本 9 的 Internet Explorer(...该控件使用机器上的 IE 版本),似乎(请参阅 cmets)此行为已修复。在这种情况下,我只能假设 IE9 'Chakra' JavaScript 引擎正在做一些与旧 js 引擎不同/不同的事情。

【讨论】:

如果在使用动态之前通过反射访问长度,动态是否有效?我想知道反射是否“修复”了通过 javascript 访问长度等问题。 @CodeNaked - 是的,确实如此!通过反射访问属性使其也可以通过动态获得。确实。似乎如果您通过其他方式在 js 或 c# 中访问它,那么它是可用的,但是如果您首先通过类型动态访问它,它会以某种方式未初始化。奇怪。 仍然令人费解的是包装器中的底层对象是什么,以及是否可以将其转换为托管类型。我将基于此再问一个问题并继续玩它...... @Fraser - 我刚刚在一个新项目中尝试了上面的代码,它运行良好!做了新的 WinForms 应用程序,将 WebBrowser 添加到 Form1,在 AssemblyInfo 中设置 ComVisible(true),并添加了您的代码。如果我按照上面的方式运行,我会收到两个显示 4 的消息框。我安装了 VS2010 SP1,不确定这是否重要。 @Fraser - 我也安装了 IE9。

以上是关于在 C# 4 中使用动态类型访问 javascript 对象的属性的主要内容,如果未能解决你的问题,请参考以下文章

C# 4.0 中的“动态”类型是做啥用的?

C# 4.0中的动态类型和动态编程

C#中4个访问修饰符(随笔)

C# 使用dynamic类型来访问JObject对象

javascript变量作用域与内存

C#反射实例学习及注意内容