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

Reflection 在C#4中使用类型dynamic访问javascript对象的属性,reflection,dynamic,c#-4.0,properties,Reflection,Dynamic,C# 4.0,Properties,我在c#中将com对象定义为动态类型,我能够非常轻松地调用方法。 但是,当我尝试访问同一对象上的属性时,我得到一个无效的强制转换异常 所讨论的对象是一个数组,从JavaScript传递到托管代码,我希望以int形式获取它的length属性 我知道我遗漏了一些奇怪的东西,因为我没有得到“不包含定义”异常,并且我可以使用reflection/InvokeMember轻松访问属性 为什么我不能将动态类型的length属性转换为int 比如说 此操作失败 dynamic com = comObje

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

所讨论的对象是一个数组,从JavaScript传递到托管代码,我希望以int形式获取它的length属性

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

为什么我不能将动态类型的length属性转换为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);
   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
这很有效

   dynamic com = comObject;
   int i = com.length; // RTBE here.
   Type type = comObject.GetType();
   int i = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);
   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
*更新*

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

var x = ["a1", "a2", "a3"];
var y = [["b1", "b2", "b3"], "a2", "a3"];
当这样的数组用于托管代码时,我可以使用dynamic类型获得长度AOK。(也就是说,这里的第一个失败的例子实际上是有效的)。但是,如果它是多维数组,如JavaScript中的以下结构

var x = ["a1", "a2", "a3"];
var y = [["b1", "b2", "b3"], "a2", "a3"];
然后,我在尝试动态访问其length属性时出错。注意,在这种情况下,我仍然可以通过反射来访问长度。在我看来,由于某种原因,当多维数组用作动态类型时,length属性没有正确映射

在我的例子中,我解决(!?)问题的方法是在传递数组之前,向数组中添加一个“length_”属性

var y = [["b1", "b2", "b3"], "a2", "a3"];
y.length_ = y.length;
现在在托管代码中,我可以按预期无误地访问此属性。远离理想,但似乎工作

   dynamic com = comObject;
   int i = com.length_; // 3!
进一步更新

好的,看来除了长度属性之外,对象索引也会丢失到动态类型中。同样,通过反射,它是可以访问的

失败

   dynamic com = comObject;
   int i = com.length; // RTBE here.
   Type type = comObject.GetType();
   int i = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);
   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
有效

   dynamic com = comObject;
   int i = com.length; // RTBE here.
   Type type = comObject.GetType();
   int i = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);
   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

简单来说,您无法通过类型dynamic访问c#中多维数组的length属性,除非您首先在JavaScript中使用了length属性

下面的简单测试非常清楚地表明了这一点。我希望这能让别人省去我在过去一天左右一直在挠头

[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)
            */             
        };
    }
}
[ComVisibleAttribute(true)]
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
webBrowser1.ObjectForScript=此;
StringBuilder html=新的StringBuilder();
html.Append(“”);
Append(“var arr1=[1,2,3,4];”;
Append(“var arr2=[arr1,2,3,4];”;
Append(“var fn1=function(){return arr1;};”;
Append(“var fn2=function(){return arr2;};”;
Append(“var fn3=function(){alert(arr2.length);}”);
html.Append(“”);
webBrowser1.DocumentText=html.ToString();
webBrowser1.DocumentCompleted+=(o,e)=>
{
动态arr1=webBrowser1.Document.InvokeScript(“fn1”);
int i=arr1.1长度;
MessageBox.Show(i.ToString());//4
//如果我在这里调用fn3,那么arr2.length*可以作为下面的int i2使用!
////webBrowser1.Document.InvokeScript(“fn3”);//4
动态arr2=webBrowser1.Document.InvokeScript(“fn2”);
int i2=arr2.5长度;
MessageBox.Show(i2.ToString());//除非调用fn3,否则您将。。。
/* 
System.MissingMemberException未由用户代码处理
Message=调用length时出错。
Source=System.Dynamic
堆栈跟踪:
位于System.Dynamic.ComRuntimeHelpers.CheckThroweException(Int32 hresult、ExcepInfo和ExcepInfo、UInt32 argErr、字符串消息)
在CallSite.Target(Closure、CallSite、ComObject)
在System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](调用站点,T0 arg0)
目标(闭包、调用站点、对象)
在System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](调用站点,T0 arg0)
*/             
};
}
}
更新


如果WebBrowser控件使用Internet Explorer的版本9(…该控件使用计算机上的IE版本),则此行为似乎已修复(请参见注释)。在这种情况下,我只能假设IE9“Chakra”JavaScript引擎与旧的js引擎做了一些额外的/不同的事情。

在现实生活中什么是
int com.property
?是JavaScript对象上的length属性……我尝试了int I=Convert.ToInt32(com.property);还有…你试过var i=com.property吗?@Taylor-是的,同样的ICE使用Vari如果你在使用dynamic之前通过反射来访问长度,那么dynamic能工作吗?我想知道反射是否“修复”了像通过javascript访问长度这样的问题。@Code裸体-是的,它修复了!通过反射访问属性也可以通过动态访问。的确如果您通过其他方式在js或c#中访问它,它似乎是可用的,但是如果您首先通过类型dynamic访问它,它不知何故是未初始化的。v、 奇怪。仍然令人困惑的是包装器中的底层对象是什么,以及我是否可以将其转换为托管类型。我将在此基础上提出另一个问题,并继续使用它…@Fraser-我刚刚在一个新项目中尝试了上面的代码,效果很好!创建新的WinForms应用程序,将WebBrowser添加到Form1,在AssemblyInfo中设置ComVisible(true),并添加代码。如果我像上面那样运行,我会收到两个显示4的消息框。我已经安装了VS2010 SP1,不确定这是否重要。@Fraser-我还安装了IE9。