Study guide
7.1 Class Architecture & Access Modifiers
Microsoft Dynamics 365: Finance and Operations Apps Developer Associate MB-500
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Cloud & Ecosystem Architecture
1.1 Cloud vs On-Premises Architecture & Deployment Typologies 1.2 Microsoft Ecosystem Integration & Extensibility Boundaries 1.3 Lifecycle Services (LCS) & Implementation Portal
2 Chapter 2: ALM, Environments & Unified Developer Experience
2.1 Package Deployment & Servicing 2.2 Unified Developer Experience (UDE) & Power Platform Admin Center 2.3 Environment Management & Governance
3 Chapter 3: Visual Studio Development & Metadata
3.1 Visual Studio Extension Models & Packages 3.2 Application Explorer & Element Designers 3.3 Database Synchronization & Visual Studio Debugging
4 Chapter 4: Azure DevOps Version Control & CI/CD
4.1 Version Control Architecture: TFVC vs Git in F&O 4.2 Branching, Merging & Conflict Resolution 4.3 CI/CD Build Pipelines & Artifact Generation
5 Chapter 5: UI Elements: Forms, Menus & Labels
5.1 Form Design & Form Patterns 5.2 Menus, Menu Items & Navigation 5.3 Label Files & Localization
6 Chapter 6: Data Model: Tables & Relational Design
6.1 Table Design & Extension 6.2 Views, Queries & Maps 6.3 Data Entity Design & Modeling
7 Chapter 7: Classes, Attributes & AOT Modifiers
7.1 Class Architecture & Access Modifiers 7.2 Attributes & Reflection 7.3 Event Handlers & Pre/Post Method Events
8 Chapter 8: X++ Core Programming & CRUD Operations
8.1 X++ Syntax, Control Flow & Global Functions 8.2 Data Manipulation & CRUD Logic 8.3 Best Practice Rules & Compiler Diagnostics
9 Chapter 9: Advanced OOP, Chain of Command & Extensibility
9.1 OOP Principles & QueryBuilder Class 9.2 Chain of Command (CoC) Implementation 9.3 Extensibility Patterns & Non-Breaking Changes 9.4 Event Handler Classes & Delegate Architecture 9.5 Variable Scoping, Declaration Placement & Object Lifetime
10 Chapter 10: F&O Frameworks & SysTest Unit Testing
10.1 SysOperation Framework Architecture 10.2 Workflow Framework & SysExtensionSerializer 10.3 SysTest Framework & Test Explorer
11 Chapter 11: Reporting Architecture & SSRS Reports
11.1 Reporting Architecture & Datastores 11.2 Report Data Providers & Contracts 11.3 SSRS Report Design, Deployment & Security 11.4 Determining When to Use a Specific Reporting Tool or Platform
12 Chapter 12: Business Intelligence, Workspaces & Electronic Reporting
12.1 Electronic Reporting (ER) Framework 12.2 Power BI Integration & Analytical Workspaces 12.3 Workspaces, Tiles, Lists & KPIs 12.4 Excel Integration & Office App
13 Chapter 13: Integration Patterns & Service Endpoints
13.1 Integration Pattern Evaluation & API Selection 13.2 Custom Services & SOAP/REST Endpoints 13.3 Consuming External Services & REST/SOAP APIs
14 Chapter 14: Data Management Framework (DMF) & Batch Processing
14.1 DMF Concepts, Projects & Sequencing 14.2 Entity Change Tracking & Recurring Integrations 14.3 Composite Entities & Batch OData API 14.4 Monitoring Entity Status, Availability & Job Execution
15 Chapter 15: Power Platform, Business Events & Azure Integrations
15.1 Dual-Write & Virtual Entities 15.2 Business Events Framework 15.3 Azure Key Vault Integration & Secure Storage
16 Chapter 16: Role-Based Security & Extensible Data Security
16.1 Role-Based Security Architecture 16.2 Extensible Data Security (XDS) Policies 16.3 Security Enforcement & Best Practices
17 Chapter 17: Performance Optimization & Data Access Patterns
17.1 Table & Form Caching Mechanisms 17.2 Temporary Tables & Set-Based Operations 17.3 Query Tuning, Indexes & Concurrency 17.4 Diagnostics: Trace Parser, Async & Sandbox Frameworks 17.5 Tuning Variable Scope, Buffer Lifetime & Memory Pressure
7.1 Class Architecture & Access Modifiers
Key Takeaways Explain This Topic AI
X++ enforces a single-inheritance object-oriented class hierarchy rooted in the common Object class, complemented by multiple interface implementation via the implements keyword.
The standard instantiation pattern in enterprise X++ encapsulates the new() constructor as protected or private, exposing public static construct() factory methods to control parameterization, caching, and polymorphism.
Modern X++ provides four access modifiers: public (accessible anywhere), protected (accessible within the class and derived classes), private (restricted to the declaring class), and internal (accessible only within the declaring compilation package/model).
The final modifier prevents class inheritance and method overriding; critically for extensions, methods declared as final cannot be wrapped using Chain of Command (CoC).
Form and table display methods compute runtime presentation values; decorating display methods with [SysClientCacheDataMethodAttribute(true)] eliminates redundant remote procedure calls between client and AOS tiers during grid rendering.
Last updated: September 2026
7.1 Class Architecture & Access Modifiers
Quick Answer: X++ is a single-inheritance, class-based object-oriented language that compiles into .NET Common Intermediate Language (CIL). Classes inherit from a single parent via extends and implement one or more contracts via implements . In enterprise X++, class instantiation is governed by the static construct() factory pattern, which hides the parameterless new() constructor to enforce controlled initialization and polymorphic subclass selection. Access visibility is governed by four modifiers: public , protected , private , and internal (which restricts visibility strictly to the declaring model/package). Method modifiers include abstract , final (which prohibits overriding and blocks Chain of Command wrapping ), and static . For user interfaces, display methods compute values dynamically and must be cached using [SysClientCacheDataMethodAttribute(true)] to prevent severe grid rendering lag. Type inspection and casting are safely handled via the is and as operators.
1. Object-Oriented Class Hierarchy in X++
Dynamics 365 Finance and Operations executes on the Microsoft .NET runtime. Every X++ class is a first-class CLR type. Understanding how X++ constructs map to object-oriented principles is foundational for MB-500 exam success.
The Root Object & Single Inheritance
Single Inheritance ( extends ): An X++ class can inherit from at most one direct parent superclass. If no superclass is specified in the declaration, the class implicitly inherits from the system root class Object .
Multiple Interface Implementation ( implements ): A class can implement multiple interfaces, providing a mechanism for polymorphic contracts without diamond-inheritance ambiguity.
Object Lifecycle and Memory Management: Objects in X++ are instantiated on the managed heap. The .NET Common Language Runtime (CLR) Garbage Collector automatically manages memory deallocation. Destructors ( finalize ) are not supported in X++; cleanup of unmanaged resources must follow the System.IDisposable pattern. // Standard class declaration illustrating inheritance and interface implementation
- public class CustInvoiceProcessor extends DocumentProcessor implements IBatchable, IDisposable
- {
- // Instance state variables
- private CustInvoiceTable invoiceTable;
- private boolean isProcessed;
- // Implementation of interface and base class methods
- }
- 2. Constructor Patterns: new() vs. Static construct() & main()
In standard object-oriented languages (like C# or Java), parameterized constructors are directly invoked using the new operator. In X++, constructor mechanics and legacy architectural patterns dictate specific instantiation conventions.
The new() Method
In X++, the constructor method is always named new() . While modern X++ supports parameters on new() , Microsoft architectural guidelines strongly discourage exposing public parameterized new() methods directly to external consumers.
Key reasons for this rule include:
Polymorphic Factory Delegation: Calling new binds the caller to a concrete class. A static factory method can inspect parameters, configuration keys, or country-region codes and return a specialized subclass.
Extensibility & Hooking: Direct instantiation via new cannot be easily intercepted by extension frameworks if subclass substitution is required.
Initialization Sequencing: A static factory can coordinate pre-initialization checks, parameter caching, and state validation before returning the instance.
The construct() Factory Pattern
Every business logic and service class in Dynamics 365 F&O should declare its new() method as protected (or private ) and expose a public static construct() method. public class SalesLineValidator
- {
- protected SalesLine salesLine;
- // Hide default constructor to prevent un-parameterized instantiation
- protected void new()
- {
- }
- // Public static factory method
- public static SalesLineValidator construct(SalesLine _salesLine)
- {
- SalesLineValidator validator;
- // Factory logic: return specialized subclass based on sales type
- switch (_salesLine.SalesType)
- {
case SalesType::ReturnItem:
- validator = new SalesLineValidator_Return();
- break;
default:
- validator = new SalesLineValidator();
- break;
- }
- validator.parmSalesLine(_salesLine);
- return validator;
- }
- public SalesLine parmSalesLine(SalesLine _salesLine = salesLine)
- {
- salesLine = _salesLine;
- return salesLine;
- }
- public boolean validate()
- {
- // Core validation logic
- return true;
- }
- }
- The main(Args _args) Entry Point
Classes intended to be invoked directly from Action Menu Items, forms, or batch jobs must declare a public static main() method accepting an Args parameter. public static void main(Args _args)
- {
- if (!_args || !_args.record() || _args.dataset() != tableNum(SalesTable))
- {
- throw error("@SYS25516"); // Record context required
- }
- SalesTable salesTable = _args.record() as SalesTable;
- SalesOrderPostManager manager = SalesOrderPostManager::construct(salesTable);
- manager.run();
- }
- 3. Visibility and Access Modifiers
Access modifiers define the encapsulation boundary for classes, methods, and member variables. Dynamics 365 F&O supports four distinct access scopes.
Comparison of Access Modifiers
Modifier Scope within Declaring Class Derived Classes in Same Model Derived Classes in External Model External Callers in Same Model External Callers in External Model
- public Accessible Accessible Accessible Accessible Accessible
- protected Accessible Accessible Accessible Blocked Blocked
- internal Accessible Accessible Blocked Accessible Blocked
- private Accessible Blocked Blocked Blocked Blocked
- The internal Modifier
Introduced to support modular architecture, the internal keyword restricts the visibility of a class, interface, or method strictly to the compilation model/package in which it is defined.
Encapsulating Private APIs: When building reusable framework models, core utility classes that should not become public contracts for ISVs or downstream customer extensions are marked internal .
Impact on Extensions: A class or method marked internal cannot be extended, subclassed, or invoked by any code residing in a different package. If an external model attempts to reference an internal class, the X++ compiler generates an accessibility violation error.
Note
Default Visibility in X++ Unlike C# where class members default to private , in X++ method declarations that omit an access modifier default to public . However, good engineering practice and Microsoft compiler linter rules mandate explicit modifier declarations on all classes, methods, and variables.
4. Class & Method Modifiers: abstract , final , and static
Modifiers govern inheritance behavior, method dispatch, and runtime execution boundaries.
Class Modifiers
abstract Class:
Cannot be instantiated directly using new or factory methods.
Acts as a partial template or contract for derived classes.
Can contain both concrete implemented methods and abstract method signatures (which declare no body and end with a semicolon).
final Class:
Prohibits inheritance. No other class can extend a class declared as final .
Used to enforce security, immutability, or deterministic execution.
Method Modifiers
abstract Method:
Can only exist inside an abstract class.
Has no implementation body; terminating with ; .
Must be overridden in any non-abstract derived concrete class.
final Method:
Prohibits overriding in derived subclasses.
static Method:
Operates on the type itself rather than an instance.
Has no this pointer; cannot access instance variables or non-static methods.
Warning
Critical Exam Rule: final Methods Block Chain of Command (CoC) Chain of Command (CoC) is the primary method-wrapping extensibility mechanism in modern Dynamics 365. However, methods declared as final CANNOT be wrapped using Chain of Command — unless the method's author explicitly opts back in with [Wrappable(true)] , the documented override that makes a final method wrappable again.
If a base application method is marked final , the compiler will reject any extension class attempting to wrap it. This is a favorite trick question on the MB-500 exam.
5. UI Method Modifiers: display , edit & Cache Optimization
In Dynamics 365 Finance and Operations, forms frequently need to display calculated or linked values that do not map directly to a single physical table column.
The display Method
A display method calculates and returns a read-only value for presentation on a form or report control. It can be declared on a Table or directly on a Form. // Declared on CustTable
- public display CustName customerFullName()
- {
- return DirPartyTable::findRec(this.Party).Name;
- }
- The Client-Server Performance Bottleneck
When a grid displaying 50 rows renders on a web browser client, an un-cached display method declared on a table executes row-by-row on the Application Object Server (AOS). If the grid is scrolled, filtered, or redrawn, the AOS recalculates the display method repeatedly, triggering separate database queries and severe network latency.
Caching Display Methods: [SysClientCacheDataMethodAttribute]
To optimize performance, developers must instruct the AOS to cache display method return values on the client tier. Modern X++ uses the [SysClientCacheDataMethodAttribute] decorator: // Cache the display method result on the client tier to avoid repeated AOS RPC calls
- [SysClientCacheDataMethodAttribute(true)]
- public display CustName customerFullName()
- {
- return DirPartyTable::findRec(this.Party).Name;
- }
true (Default Parameter): Caches the return value on the client tier. The method executes once when the record is fetched and is not re-executed unless the record buffer is refreshed or updated.
Form Initialization Caching ( cacheAddMethod ): On form datasources, developers can also register table display methods into the form's cache during init() : [ExtensionOf(formDataSourceStr(CustTable, CustTable))]
- final class CustTableForm_Extension
- {
- public void init()
- {
- next init();
- this.cacheAddMethod(tableMethodStr(CustTable, customerFullName));
- }
- }
- The edit Method
An edit method provides read/write access to calculated or unmapped values. It takes a boolean parameter indicating whether the user is setting or getting the value: // Declared on a Table or Form Datasource
- public edit boolean editOverrideCreditLimit(boolean _set, boolean _newValue)
- {
- if (_set)
- {
- this.CreditMaxOverride = _newValue;
- this.modifiedField(fieldNum(CustTable, CreditMaxOverride));
- }
- return this.CreditMaxOverride;
- }
- 6. Polymorphism, Interfaces & Safe Casting ( is and as Operators)
Polymorphism enables treating instances of different subclasses through a common superclass or interface reference.
- Defining and Implementing Interfaces
- Interfaces define public contractual signatures without implementation: public interface IPaymentGateway
- {
- public boolean authorizePayment(AmountCur _amount, CurrencyCode _currency);
- public TransId capturePayment(AmountCur _amount);
- }
- public class StripePaymentGateway implements IPaymentGateway
- {
- public boolean authorizePayment(AmountCur _amount, CurrencyCode _currency)
- {
- // Stripe API integration
- return true;
- }
- public TransId capturePayment(AmountCur _amount)
- {
- return "STRIPE-" + guid2Str(newGuid());
- }
- }
- Safe Type Checking and Casting: is and as
Prior to modern X++, casting an object to an incompatible type resulted in an unhandled CLR InvalidCastException that halted execution. Modern X++ provides the safe is and as keywords:
is Operator (Type Verification):
Evaluates whether an object instance is compatible with a given class or interface type.
Returns a boolean true or false without throwing an exception.
as Operator (Safe Casting):
Attempts to cast the object instance to the specified type.
If the object is incompatible, it evaluates to null instead of raising a runtime exception. public static void processGateway(Object _unknownObject)
- {
- // 1. Safe type checking using 'is'
- if (_unknownObject is IPaymentGateway)
- {
- // 2. Safe casting using 'as'
- IPaymentGateway gateway = _unknownObject as IPaymentGateway;
- if (gateway != null)
- {
- gateway.authorizePayment(150.00, "USD");
- }
- }
- else
- {
- warning("Provided object does not implement IPaymentGateway.");
- }
- }
- 7. Scenario Walk-Through: Building an Extensible Tax Calculation Framework
- Scenario Description
Contoso Retail requires a decoupled, polymorphic tax calculation framework. The system must support standard domestic sales tax, value-added tax (VAT) for European entities, and zero-rated export tax. Direct instantiation of concrete tax engines must be blocked, and the solution must dynamically return the appropriate calculator based on the delivery country code.
Step-by-Step Implementation Flow
Define the Interface ( ITaxCalculator ):
Declare method AmountMST calculateTax(AmountMST _taxableAmount, TaxCode _taxCode) .
Declare Abstract Base Class ( TaxCalculatorBase ):
Declare as public abstract class TaxCalculatorBase implements ITaxCalculator .
Define protected void new() to prohibit direct instantiation.
Declare protected AddressCountryRegionId countryRegionId .
Declare public abstract AmountMST calculateTax(AmountMST _taxableAmount, TaxCode _taxCode); .
Implement Static Factory Method ( construct ):
In TaxCalculatorBase , implement public static TaxCalculatorBase construct(AddressCountryRegionId _countryId) .
Evaluate _countryId using a switch block and instantiate TaxCalculator_Domestic , TaxCalculator_VAT , or TaxCalculator_Zero .
Implement Concrete Subclasses:
Implement concrete calculation logic in each subclass.
Consume Polymorphically via Safe Casting:
In the posting pipeline, obtain the calculator via TaxCalculatorBase::construct(address.CountryRegionId) and invoke calculateTax() .
8. Real-World Exam Traps: Class Architecture & Access Modifiers
Warning
Exam Trap 1: Attempting Chain of Command on a final Method or Class An exam scenario asks why a developer's class extension fails to compile when wrapping a method on a standard class. If the target method or class is decorated with the final keyword, Chain of Command is strictly prohibited by the compiler. The developer must use pre/post events (if hookable) or raise an extensibility request.
Warning
Exam Trap 2: Omitting Display Method Caching on Form Grids A scenario describes a form grid where scrolling is sluggish and users experience notable latency. The question asks how to optimize the display method. The correct solution is adding [SysClientCacheDataMethodAttribute(true)] to the table display method or calling cacheAddMethod() in the form datasource init() method.
Warning
Exam Trap 3: Direct Class Hard-Casting Causing Unhandled Exceptions An exam question shows code like CustInvoiceTable invoice = (CustInvoiceTable)_commonRecord; and asks what happens if _commonRecord is a SalesTable . Hard casting throws an unhandled CLR InvalidCastException . The robust pattern uses _commonRecord as CustInvoiceTable followed by a null check.
Warning
Exam Trap 4: Calling internal Classes from an External Model A question presents two packages: ModelA (which defines an internal class InventoryInternalUtility ) and ModelB (which references ModelA ). If code in ModelB attempts to call InventoryInternalUtility::run() , the project fails to compile because internal restricts visibility strictly to ModelA .