Extensible X++ – Method signatures


Method signatures are not extensible – and will not be.


Extensible parameters would be intrusive – in so many ways:

  • Breaks derived classes (signatures of overridden methods must match),
  • Requires recompilation,
  • No side-by-side support.


Here are some alternatives.


The request for adding a new parameter to a method as an extension is surfacing quite frequently. A method's signature is the contract between caller and method.  When new parameters are added, then callers can provide additional data that the method can act on. In isolation; a request for an extra parameter makes no sense. An extra parameter will not help anyone unless someone is passing it in, and someone is acting on it.

In the land of extensibility there are restrictions:

  • You cannot change the places a given method is called, that would require overlayering.  But you can call the method from your own code (with control over the parameters).
  • You cannot change the implementation of the method, that would require overlayering. But often you can add logic pre/post or wrap using Chain-of-command.

With these constraints, here are some options:

Option 1: "Method overloading"


You need to pass extra information from your code to an existing method and act on it in a pre/post handler.


X++ doesn't support overloading methods – but you can mimic the behavior by creating a new method (with new name) in an extension class. The extension method can take additional parameters and call the original method; with your logic before and after calling the original method.


Here is an example of "overloading" the insert method on CustTable. Notice the first 3 parameters are identical to the parameters on the original insert() method.

Option 2: Class state


You need to pass extra information from your code to a method on a class that is called via other method(s) on the class.


X++ now supports adding class state via an extension. You can use this to store the extra information.


Here is an example adding state and wrapping a method.

Here is an example using this. The standard implementation of initFromItemOrCategory() calls initFromInventTable().


Option 3: Disposable context


You need to pass extra information from your code to a pre/post handler somewhere downstream.


It can be tempting to store the extra information in a global cache or variable. There is a better approach, which avoids stale data, and is type-safe. Create a singleton class for the context. The class must implement System.IDisposable, so it is disposed when it goes out of scope. The receiving code can access the singleton instance to extract the information.


Here is an example of the context class:

Here is an example of the calling code:

Here is an example of the consuming code. The standard implementation of CustTable.Insert() calls DirPartyTable::createNew() – the example uses Chain-of-command to wrap the createNew method, and then accesses the context to get the information.


Transferring state from one arbitrary place to another using a global variable (which the context class is) can lead to future logical errors – that can be very hard to detect and fix. My recommendation would be to limit the usage to situations where the scope and consequences are manageable.

Option 4: Request new extension points

Now, the above mechanisms cannot solve the cases where existing callers need to provide more data, or where the existing method implementation needs to act on the data. That would require overlayering. If you discover a need for this or another case where you are not comfortable with the above options, please log an extension request explaining your scenario. If you've read so far, it should now be obvious that you need more than changing a method's signature – you need an extension point, the implementation of this might require changing a method's signature – but that is just a technical detail.


P.S. If you have other suggestions for how to solve situations like these, please leave a comment.



Comments (4)

  1. Dick de Jong says:

    Dear Michael,

    I am not every day coding in ax, but I have serious concerns here.. I understand the drive towards the ‘open-close’ paradigm .. Open for extensions, closed for modifications … But …I very, very much agree with mcounter ..

    You provide 3 options:

    a) On option 2 (class state), I directly say a big NO to global class members since I want to keep scope as limited as possible to maintain quality code and avoid maintenance issues. So class introducing class member I will suggest never to use. In a customization project you could allow bcz there it could be of small impact but in ISV solutions what we do it is definitely a no-go. Sorry for being blunt, but I feel you will also hesitate to use this approach in your code.

    b) On option 3 .. that is in fact more or less the same as the class member except that the member is not in the class itself but in some external cache … we could add a pre- handler and in a pre-handler issue an object with GUID identifier and then store that in cache and set that as member on the class (since the class must know which context object instance to retrieve from cache) but you have to be very careful in clearing state of your cache object bcz it could very well be that method is called multiple times in same thread (sequentially) and next time it should not use the wrong context object .. what if you e.g. have exception, you must clear context or sometimes not clear context bcz if retry comes you can reuse possibly .. this requires very accurate coding and is very much open to possible errors than just passing a method parameter … But I prefer this one above the global class member still.
    c). the overloading could be possible but I think you get a mess of ‘copied’ code blocks to extension classes. Bcz we do not only pass an extra parameter but also somewhere in the middle of the method we use that parameter and we change the standard ax code flow …
    d). extension points will be slowing down the process of development too much I feel.. and it will disrupt the structure of MS std code possibly .. Thing is that currently methods have quite a coarse granularity (in ax) and that enforces you (in case of override) to override a lot …

    You mean to say that method delegates is not an option? If so, I agree, bcz method delegates change also the standard ax class (call to delegate) … and then anyway it is a customization and not a closed principle.

    1. Thanks Dick.

      The overall goal of extensibility is to get our shared customer running on the latest release always. This means that extra care and attention to writing robust extensions are required. Significantly more care than when customizing via overlayering. Ultimately; we want to be able to do a binary replace of AppSuite – and everything still works.

      The AX extensibility model is quite powerful – too powerful in some cases. When using this toolbox, you are responsible for building a solution that can “survive” an AppSuite upgrade as-is. Any brittleness you build in, like dependencies on call stack, extensions to methods that are not coherent with the standard method will cause disruption to our customers. One example of the latter is to change the global company context in an extension method. We trust you to make strong, durable and lasting solutions.

      I agree there is a turn-around time on extension requests; which can be obstructive to development. The sooner you log them the sooner you’ll get them; plan for this delay.

      Regarding option 2: The state is class instance specific – not global. There are cases where the state is coherent with the class and its purpose. Here I would not have concerns.
      Regarding option 3: You should really use IDisposable, like outlined, instead of guids and potentially stale caches.
      Regarding delegates: Chain-of-command is large replacing the need for delegates (and requests for adding these).

  2. mcounter says:

    Option 3 looks very risky, and will work reliable only in case different user sessions work in separate .NET domains and classes not share static variables across domains. The same must be true for both main AOS instances – IIS-hosted and Batch service. Only MS has information how it’s implemented on core level. If MS can confirm that each user-session and batch thread is domain-independent and not share static objects, this is really useful approach. Otherwise it leads to hardly-discoverable errors caused by using same variable across session.

    I suggest Microsoft design reliable framework which allow discover and impact on standard code states from external trusted extensions. Very often we use additional parameters to change standard method behavior only in case it’s called from particular standard place. Today we cannot do it without overlayering. But if only we have access to call-stack and it variables in the same way we have it from Visual Studio debugger, it will be power way to avoid overlayering, parameters extension and code duplicates.

    Example: Method B.m1() is called from classes A1 and A2, … An and return some calculated amount. But we want change it behavior only if it’s called from class A2.m2(). In AX2012 we could add one-more parameters or call separate method. But in AX7 we want avoid overlayering. We use chain of command and wrap B1.m1() method. After next call we want change returned value, but only in case it was called from A2.m2() and probably use some variables from A2.m2(). We have no any trusted way to do that, but it’s technically possible, because this information is accessible from debugger and can be accessible from X++ code.

    Sure, it sounds like dangerous approach, but really not better than overlayering.

    1. Hi mcounter,
      Thanks for the comment. There is no sharing of static members with other threads/batches/AOSes/etc. This was also mentioned in this blog post in one of the comments: https://blogs.msdn.microsoft.com/mfp/2015/12/08/x-in-ax7-static-members/
      I’m not getting your example – what will that enable that option 3 above doesn’t? I discourage dependencies on call stacks – those are subject to change, and will break your code when upgrading. The disposable context is more reliable.
      Happy extending,

Skip to main content