已经讲过可以通过Proxy class从C#使用X++的类,反过来从X++使用CLR的类型当然也是可以的,这在以前版本的AXAPTA 4.0、AX 2009就开始支持了,这里把要注意的问题做一简单的归纳。
X++使用CLR类的静态方法需要使用“::”而不是C#中的“.”,这和X++调用X++类的静态方法是一样的:
System.String netString=System.Convert::ToString("xxx");
从X++的数据类型到CLR的元类型或者从CLR的元类型到X++的数据类型,会自动进行隐式的marshal转化,下表是两者类型的对应关系:
X++ type |
CLR type |
---|---|
boolean |
|
date |
|
int |
|
int64 |
|
str |
|
guid |
|
Real |
X++的int类型是不会转化成CLR的Int64的,CLR的Int32也是不会转成X++的int64,反过来从64位到32位整数也是一样。X++的real类型同时对应CLR的Single和Double两种,X++的real类型可以直接赋值给CLR的Single类型,在AX2009中也可以直接赋值给Double类型,但是以后的版本中可能会改变,AX2012提供函数Global::real2double(xppReal)将X++的real类型转成CLR的Double类型。
X++ timeofday没有对应的CLR元类型,它内部其实使用整数类型来表示数据的。同样X++的utcdatetime也没有对应的CLR类型,AX2012提供Global::utcDateTime2SystemDateTime()和Global::CLRSystemDateTime2UtcDateTime()两个方法来相互转换。AX2012还提供DateTimeUtil::tostr()从utcdatetime转换成字符串,和DateTimeUtil::parse()从字符串转换成utcdatetime,需要注意的是CLR的System.Convert.ToString()或者System.DateTime.ToString()得到字符串是不能直接被DateTimeUtil::parse()解析的,时间字符串的格式要使用ISO的标准24小时格式“yyyy-mm-ddThh:mm:ss”,比如当前的时间应该为“2012-09-11T10:01:05”,实际上这样的格式的时间是可以直接赋值给X++的utcdatetime而不需要DateTimeUtil::parse()做额外的转换,比如:
utcdatetime xppIsoDttm = 2012-09-11T10:01:05;
在X++中CLR的值类型实际上是以引用保存的,这意味着除了“=”赋值操作符外,不能使用“==”或者“>”等逻辑运算符来比较两个CLR值类型,也不能使用位操作符“&”及“|”。比如两个System.Int32的变量a和b,这样判断“a>=b”是不正确的,相应的要使用System.Int32.CompareTo()方法。
X++可以使用CLR的数组,同样使用“[]”定义一个数组,内部创建System.Array的CLR类型,但是不能使用“[]”访问CLR数组的元素,而要使用GetValue()、SetValue()等方法:
static void JobTestNetArray() { System.Int32[] iNetNumbers; // .NET Framework array int iXppNumbers[2]; // X++ array int iXppNum,iXppArrayLength,i; ; info("Next, .NET Framework array by special X++ syntax."); //-------- .NET Framework array by special X++ syntax ------ iNetNumbers = new System.Int32[2](); // Note the () at end. iNetNumbers.SetValue(100, 0); // Resembles iNetNumbers[0]=100. iNetNumbers.SetValue(101, 1); iXppArrayLength = iNetNumbers.get_Length(); for (i = 0; i < iXppArrayLength; i++) { iXppNum = iNetNumbers.GetValue(i); // Resembles iNetNumbers[i]. info(int2str(iXppNum)); } info("Next, X++ Native array."); //----------- X++ Native array ------ iXppNumbers[1] = 2201; iXppNumbers[2] = 2202; for (i = 1; i <= dimOf(iXppNumbers); i++) { info(int2str(iXppNumbers[i])); } }
X++数组和CLR数组有很多的不同:X++数组下标从1开始,CLR数组下标从0开始;X++数组定义了就可以使用,CLR数组必须使用new关键字构建;X++数组索引必须是X++的整数类型,CLR数组索引则可以是CLR的整数类型,也可以是X++的整数类型;X++数组是一维的,CLR数组可以是多维的;X++的数组元素只能是str或者int类型,CLR数组元素则可以是任何CLR类型,但不能是X++的类型;X++数组的长度在定义时就确定了,CLR数组的类型在new时确定,new时必须指定数组长度,否则报语法错不能编译。
X++的类型可以作为函数参数传递给CLR的函数,比如CLR函数参数原型为System.String,可以将X++的str传递给这个函数,但是反过来是不可以的,不能把CLR的类型传递给需要X++类型参数的函数。对于C# ref、out标识的引用参数,X++提供byref关键字表示引用参数传递。需要注意的是那种值传递的对象类型,在传递给被调用函数前会将对象类型的指针复制一份传递,看这样一个例子:
class MyClass //X++ { public static void CallerMethodByValueObject() // X++ { TestProxyClass.MyEntity meTest1; TestProxyClass.MyEntity meTest2; int i; ; meTest1 = new TestProxyClass.MyEntity(1); meTest2 = new TestProxyClass.MyEntity(33); MyClass::CalledMethodByValueObject(meTest1, meTest2); i=meTest1.GetCounter(); if (i == 1) { info("Good, == 1"); } i=meTest2.GetCounter(); if (i == 36) { info("Good, == 36"); } TestProxyClass.MyEntity::CalledMethodByValueObject(meTest1,meTest2); //X++ i=meTest1.GetCounter(); if (i == 1) { info("Good, == 1"); } i=meTest2.GetCounter(); if (i == 39) { info("Good, == 39"); } } static public void CalledMethodByValueObject // X++ ( TestProxyClass.MyEntity meTest1 // by value , TestProxyClass.MyEntity meTest2 // by value ) { ; meTest1 = new TestProxyClass.MyEntity(555); // Caller can not detect. meTest2.AddToCounter(3); // Caller can detect. } } public class MyEntity //C# { private int _counter; public MyEntity(int counter) { _counter = counter; } public int GetCounter() { return _counter; } public void AddToCounter(int c) { _counter += c; } static public void CalledMethodByValueObject ( MyEntity meTest1 // by value , MyEntity meTest2 // by value ) { ; meTest1 = new MyEntity(555); // Caller can not detect. meTest2.AddToCounter(3); // Caller can detect. } }
MyEntity是一个C#的类,在X++的MyClass::CallerMethodByValueObject()方法中以值的方式分别传递meTest1、meTest2给X++的MyClass::CalledMethodByValueObject和C#的MyEntity.CalledMethodByValueObject两个静态方法,主程序中显示meTest1的结果都是1,这说明被调用函数中修改的meTest1只是传入变量指针的拷贝,不会影响到主程序中的变量,不管是被调用函数是X++还是C#都是一样,其实在C#中调用MyEntity.CalledMethodByValueObject也是得到相同的结果,这点上X++和C#是一致的。
X++使用的CLR程序集要么是在AOT的Reference节点下手工引用,或者是在GAC,在客户端AX会提示把引用的程序集放到client的bin目录下,默认C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin,服务端是在C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin,注意和我们把c# project添加到AOT后的部署路径是不同的。虽然可以使用server关键字让类的静态方法在AOS运行,但是试图从这些server方法返回一个CLR对象到client是不可以的,收到的结果是CLRojbect未初始化异常。
X++可以使用try...catch捕捉CLR异常,提供了专门的异常类型Exception::CLRError,在捕捉到CLR异常后可以使用ClrInterop::getLastException()获得一个CLR的异常对象System.Exception,调用它的get_InnerException()可以进一步获得此异常的详细信息。
X++的系统类DictMethod提供方法clrParameterType、clrReturnType、clrVarType来获取X++方法的参数CLR类型、函数返回值CLR类型和函数本地变量CLR类型。
更多有关.NET Interop from X++的内容见http://msdn.microsoft.com/en-us/library/cc598160。