Export (0) Print
Expand All

How to: Marshal Between X++ and CLR Primitive Types [AX 2012]

Updated: November 18, 2013

Applies To: Microsoft Dynamics AX 2012 R3, Microsoft Dynamics AX 2012 R2, Microsoft Dynamics AX 2012 Feature Pack, Microsoft Dynamics AX 2012

In Microsoft Dynamics AX, the X++ language does implicit conversion or marshaling between several X++ primitive types and their counterpart types managed by the common language runtime (CLR). This means that the X++ assignment operator, the single equal sign (=), can be used between certain pairings of an X++ type with a .NET Framework CLR type.

Implicit marshaling with the X++ assignment operator works in both directions, either from X++ types to CLR types, or from CLR to X++.

A .NET managed assembly might provide a useful method that returns a System.String object. You can call the method and capture the returned System.String in a variable of that type that you declare in your X++ code.

But if your next step is to pass that string into an X++ method that takes a str, you must first marshal the System.String into a str.

For more information, see How to: Substitute Primitive Parameter Types Between the CLR and X++.

Each row in the following table lists a pairing of types that are implicitly marshaled by the X++ assignment operator.

NoteNote

There is no implicit marshaling between the X++ utcdatetime and .NET Framework System.DateTime type. For more information about how to convert between utcdatetime and System.DateTime, see How to: Convert Between utcdatetime and System.DateTime.

The following code sample shows the marshaling between the .NET Framework System.Boolean type and its X++ counterpart boolean.

static void JobBooleanMarshal(Args _args) // X++ job.
{
    System.Boolean netBool; // .NET
    boolean xppBool; // X++
    ;
    // Marshal .NET to X++.
    xppBool = false;

    netBool = true;
    xppBool = netBool; // Marshals.
    if (true == xppBool)
    {
        info("A1. Good, .NET was marshaled to X++.");
    }
    else
    {
        info("A2. Bad, .NET was not marshaled to X++.");
    }
    // Marshal X++ to .NET.
    netBool = true;
    
    xppBool = false;
    netBool = xppBool; // Marshals.
    
    xppBool = true;
    xppBool = netBool;
    if (false == xppBool)
    {
        info("B1. Good, X++ was marshaled to .NET.");
    }
    else
    {
        info("B2. Bad, X++ was not marshaled to .NET.");
    }
}
/****** Actual infolog output
Message (01:08:32 pm)
A1. Good, .NET was marshaled to X++.
B1. Good, X++ was marshaled to .NET.
******/

The following code sample shows the marshaling between the .NET Framework System.DateTime type and the X++ date type. From System.DateTime, there is no marshaling to the X++ utcdatetime type, and there is no marshaling to the timeOfDay extended data type (which is an int).

static void JobDateTimeMarshal(Args _args)
{
    System.DateTime netDttm;
    date xppDate;
    str strTemp;
    ;
    // Marshal .NET to X++.
    netDttm = new System.DateTime(1988,7,20 ,13,44,55);
    xppDate = netDttm; // Marshals.
    if (date2str(xppDate,321, 2,2,2,2,4) == "1988.07.20")
    {
        info("A1. Good, .NET was marshaled to X++.");
    }
    else
    {
        info("A2. Bad, .NET was not marshaled to X++.");
    }
    // Marshal X++ to .NET.
    xppDate = 25\11\2002;
    netDttm = xppDate; // Marshals.
    strTemp = netDttm.ToString("s");
    if (strTemp == "2002-11-25T00:00:00")
    {
        info("B1. Good, X++ was marshaled to .NET.");
    }
    else
    {
        info("B2. Bad, X++ was not marshaled to .NET.");
    }
}

The following code sample shows the marshaling between the .NET Framework System.Int32 type and its X++ counterpart int.

static void JobInt32Marshal(Args _args)
{
    System.Int32 netInt;
    int xppInt;
    ;
    // Marshal .NET to X++.
    netInt = 33;
    xppInt = netInt; // Marshals.
    if (33 == xppInt)
    {
        info("A1. Good, .NET was marshaled to X++.");
    }
    else
    {
        info("A2. Bad, .NET was not marshaled to X++.");
    }
    // Marshal X++ to .NET.
    netInt = 0;

    xppInt = 444;
    netInt = xppInt; // Marshals.

    xppInt = 0;
    xppInt = netInt;
    if (444 == xppInt)
    {
        info("B1. Good, X++ was marshaled to .NET.");
    }
    else
    {
        info("B2. Bad, X++ was not marshaled to .NET.");
    }
}

Automatic marshaling works between the X++ int64 type and the .NET Framework type System.Int64, just as marshaling works between the X++ int and the .NET System.Int32.

An X++ int does not marshal to a System.Int64, nor the reverse. A System.Int32 does not marshal to an X++ int64, nor the reverse.

The following code sample shows the marshaling between the .NET Framework System.String type and its X++ counterpart str.

Caution noteCaution

An error occurs if an assignment is made to an X++ str variable from a System.String variable that is currently null.

static void JobStringMarshal(Args _args)
{
    System.String netString;
    str xppString;
    ;
    // Marshal .NET to X++.
    netString = "cat";
    xppString = netString; // Marshals.
    if ("cat" == xppString)
    {
        info("A1. Good, .NET was marshaled to X++.");
    }
    else
    {
        info("A2. Bad, .NET was not marshaled to X++.");
    }
    // Marshal X++ to .NET.
    netString = "";

    xppString = "dog";
    netString = xppString; // Marshals.

    xppString = "";
    xppString = netString;
    if ("dog" == xppString)
    {
        info("B1. Good, X++ was marshaled to .NET.");
    }
    else
    {
        info("B2. Bad, X++ was not marshaled to .NET.");
    }

/***** Actual infolog output
Message (06:27:19 am)
A1. Good, .NET was marshaled to X++.
B1. Good, X++ was marshaled to .NET.
*****/
}

As an option in X++, str variables can be declared with a maximum length. The following str declaration sets the maximum length at 8. The assignment would end with the value of myStr being "12345678", with the "9" being truncated:

str 8 myStr = "123456789"; // The 9 is truncated.

The System.String type in .NET Framework does not have a property that corresponds to the maximum length of an X++ str, and string truncation does not occur. This feature difference could lead to different behavior at run time in the following cases:

  • X++ str is marshaled to .NET Framework.

  • X++ is compiled to .NET Framework common intermediate language (CIL), and is run as CIL.

When X++ is run as p-code rather than as CIL, instances of str truncation can be logged by Microsoft Dynamics AX. For more information, see the logstrtrunc parameter in Client configuration commands.

The following code sample shows the marshaling between the .NET Framework System.Guid type and its X++ counterpart guid.

static void JobGuidMarshal(Args _args)
{
    System.Guid netGuid;
    guid xppGuid;
    str sGuid1
        ,sGuid2;
    ;
    sGuid1 = "10001000-1000-1000-1000-100010001000";
    sGuid2 = "20002000-2000-2000-2000-200020002000";
    // Marshal .NET to X++.
    netGuid = Global::guidFromString(sGuid1);
    xppGuid = netGuid; // Marshals.
    if (xppGuid == Global::guidFromString(sGuid1))
    {
        info("A1. Good, .NET was marshaled to X++.");
    }
    else
    {
        info("A2. Bad, .NET was not marshaled to X++.");
    }
    // Marshal X++ to .NET.
    xppGuid = Global::guidFromString(sGuid2);
    netGuid = xppGuid; // Marshals.
    xppGuid = Global::guidFromString(sGuid1);
    xppGuid = netGuid;
    if (xppGuid == Global::guidFromString(sGuid2))
    {
        info("B1. Good, X++ was marshaled to .NET.");
    }
    else
    {
        info("B2. Bad, X++ was not marshaled to .NET.");
    }
}

The following code sample shows the marshaling between the .NET Framework types System.Single and System.Double, and their mutual X++ counterpart real. The sample shows that the X++ real does marshal in both directions, with both .NET Framework types.

static void JobRealMarshal(Args _args)
{
    str xppStr;
    real xppReal;
    System.Double netDouble;
    System.Single netSingle;
    ;
    xppReal = 1.2305e+6;

    netSingle = xppReal;
    xppReal = 0.0;
    xppReal = netSingle;


    // Works in AX 2009, but not in later versions.
    //netDouble = xppReal;

    // Works in AX 2012, but
    // this method does not exist in earlier versions.
    netDouble = Global::real2double(xppReal);


    xppReal = 0.0;
    xppReal = netDouble;

    netSingle = xppReal;
    xppStr = System.Convert::ToString(netSingle);
    info(xppStr);
}
/***** Actual infolog output
Message (01:39:56 pm)
1230500
*****/


The following line of X++ code fails to compile. The reason is that the literal 98.76 is treated as a System.Decimal, which cannot be directly assigned to a System.Double.


 System.Double netDouble = 98.76; // This line of X++ code fails to compile. 

In some cases with X++, an enum element value returned from a .NET Framework method is treated as a System.Int32 value in X++. You can call the System.Enum::ToObject method to convert the Int32 value into an enum element value.

The following X++ code example is artificially forced for simplicity. But it demonstrates how you can convert the Int32 value into an element value. In particular, it demonstrates the calls to the following two methods:

  • System.Object.GetType

  • System.Enum::ToObject

static void GmEnumTest37Job(Args _args) // X++ job, in AOT > Jobs.
{
    System.AttributeTargets  // Full .NET type name.
         enumAttribTarg1
        ,enumAttribTarg2;
    System.Type netSysType;
    System.Int32 netInt = -125;
    
    enumAttribTarg1 = System.AttributeTargets::Assembly;
    netInt = System.Convert::ToInt32(enumAttribTarg1);
    
    if (enumAttribTarg1 == netInt)
        { info("A_TRUE:    (enumAttribTarg1 == netInt)"); }
    else
        { info("A_FALSE:   (enumAttribTarg1 == netInt)"); }
    
    //  This conversion technique would fail to compile:
    //enumAttribTarg2 = netInt as System.AttributeTargets;
    
    //  This conversion technique succeeds:
    netSysType = enumAttribTarg1.GetType();
    enumAttribTarg2 = System.Enum::ToObject
        (netSysType, netInt);
    
    if (enumAttribTarg1 == enumAttribTarg2)
        { info("B_TRUE:    (enumAttribTarg1 == enumAttribTarg2)"); }
    else
        { info("B_FALSE:   (enumAttribTarg1 == enumAttribTarg2)"); }
}
/*****  Pasted from output:
Message (04:24:27 pm)
A_FALSE:   (enumAttribTarg1 == netInt)
B_TRUE:    (enumAttribTarg1 == enumAttribTarg2)
*****/

When you work with .NET primitive types in X++ code, you can use the X++ equal sign (=) assignment operator. However, no other operators can be used with CLR primitives. For instance, you cannot use the comparison operators (such as == or >). Also, you cannot use bitwise operators (such as & or |).

For more information about X++ operators with .NET primitive types, see Operators for CLR Primitive Types.


Announcements: To see known issues and recent fixes, use Issue search in Microsoft Dynamics Lifecycle Services (LCS).

Community Additions

ADD
Show:
© 2014 Microsoft