{"id":1450,"date":"2026-04-30T06:14:35","date_gmt":"2026-04-30T06:14:35","guid":{"rendered":"https:\/\/cloudclif.com\/blogs\/?p=1450"},"modified":"2026-05-01T15:54:18","modified_gmt":"2026-05-01T15:54:18","slug":"delegates-in-x-the-underused-pattern-for-clean-extensibility","status":"publish","type":"post","link":"https:\/\/cloudclif.com\/blogs\/2026\/04\/30\/delegates-in-x-the-underused-pattern-for-clean-extensibility\/","title":{"rendered":"Delegates in X++: The Underused Pattern for Clean Extensibility\u00a0"},"content":{"rendered":"\n<p><em>Reading time: ~8 minutes.<\/em>&nbsp;<\/p>\n\n\n\n<p>Delegates are the cleanest extension mechanism in X++. They are also the least taught. Most F&amp;O developers reach for CoC or event handlers first, write working code, and never seriously consider whether a delegate would have been the better answer \u2014 or whether the situation called for publishing one of their own. This post is about both halves: subscribing to delegates that already exist, and writing delegates in your own code so other extensions can plug in cleanly.&nbsp;<\/p>\n\n\n\n<p>If you\u00a0haven&#8217;t\u00a0read the foundation posts: CoC mechanics <a href=\"https:\/\/cloudclif.com\/blogs\/2026\/04\/26\/chain-of-command-in-d365-fo-a-practitioners-guide-to-when-and-when-not-to-use-it\/\" data-type=\"post\" data-id=\"1398\">Chain of Command in D365 F&amp;O<\/a>, events vs CoC\u00a0<a href=\"https:\/\/cloudclif.com\/blogs\/2026\/04\/27\/event-handlers-vs-chain-of-command-the-real-decision-framework-for-2026\/\" data-type=\"post\" data-id=\"1409\">Event Handlers vs Chain of Command<\/a>.\u00a0<\/p>\n\n\n\n<p><strong>What a delegate&nbsp;actually is<\/strong>&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"683\" height=\"1024\" src=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142-683x1024.png\" alt=\"\" class=\"wp-image-1459\" srcset=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142-683x1024.png 683w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142-200x300.png 200w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142-768x1152.png 768w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142-585x878.png 585w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f5fe3852-7c2b-4e47-b582-5b6072f89142.png 1024w\" sizes=\"(max-width: 683px) 100vw, 683px\" \/><\/figure>\n\n\n\n<p>A delegate in X++ is a&nbsp;method&nbsp;declaration whose body is empty by design. The publisher of the delegate&nbsp;isn&#8217;t&nbsp;writing logic \u2014&nbsp;they&#8217;re&nbsp;declaring an extension point. When code execution reaches the call site, the delegate fires, every&nbsp;subscriber&#8217;s&nbsp;handler runs, and execution continues.&nbsp;<\/p>\n\n\n\n<p>Microsoft Learn&#8217;s Events and delegates reference shows the&nbsp;canonical&nbsp;shape:&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ In the publisher class \u2014 declares the extension point \ndelegate void RentalTransactionAboutTobeFinalizedEvent( \n    FMRental fmRentalRecord, struct RentalConfirmation) \n{ \n} \n\/\/ In a subscriber class \u2014 handles the event when it fires \n&#91;SubscribesTo( \n    classstr(FMRentalCheckoutProcessor), \n    delegatestr(FMRentalCheckoutProcessor, RentalTransactionAboutTobeFinalizedEvent))] \n\npublic static void RentalFinalizedEventHandler( \n    FMRental rentalRecord, Struct rentalConfirmation) \n{ \n    \/\/ your custom logic runs when the publisher fires the delegate \n} <\/code><\/pre>\n\n\n\n<p>Two non-obvious things on that snippet. First, the delegate&#8217;s body is empty \u2014&nbsp;that&#8217;s&nbsp;not a&nbsp;stub,&nbsp;it&#8217;s&nbsp;the entire definition. Second, delegates always have&nbsp;void&nbsp;return type. Microsoft is explicit on this in the migration-delegates reference:&nbsp;<em>&#8220;delegates do not have a return&nbsp;value,&nbsp;an&nbsp;EventHandlerResult&nbsp;is passed as a parameter to provide access to the needed result value after the delegate has returned.&#8221;<\/em>&nbsp;That void-only rule shapes everything about how delegates carry information back to their caller \u2014 covered in the next section.&nbsp;<\/p>\n\n\n\n<p><strong>EventHandlerResult&nbsp;\u2014 how a delegate returns a value without returning a value<\/strong>&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"1024\" src=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-1024x1024.png\" alt=\"\" class=\"wp-image-1462\" srcset=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-1024x1024.png 1024w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-300x300.png 300w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-150x150.png 150w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-768x768.png 768w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-1170x1170.png 1170w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31-585x585.png 585w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/f2e34b1c-017a-4704-aaf5-750cc14d0f31.png 1254w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Because delegates&nbsp;can&#8217;t&nbsp;return values directly, the publisher passes a result-carrier object as a parameter. Subscribers populate it; the publisher reads it after the delegate fires. The pattern is documented across Microsoft Learn&#8217;s Respond by using&nbsp;EventHandlerResult&nbsp;and&nbsp;EventHandlerResult&nbsp;classes references.<\/p>\n\n\n\n<p>The carrier comes in three\u00a0flavours, all from the platform:\u00a0<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>EventHandlerResult\u00a0\u2014\u00a0generic;\u00a0subscriber calls\u00a0_result.result(value)\u00a0to set whatever value type matches the agreed contract.\u00a0<\/li>\n\n\n\n<li>EventHandlerAcceptResult\u00a0\u2014 restricted to accept-only\u00a0signalling; subscriber calls\u00a0_result.accept(). You cannot\u00a0reject through\u00a0this type.\u00a0<\/li>\n\n\n\n<li>EventHandlerRejectResult\u00a0\u2014 the inverse, restricted to\u00a0_result.reject(). You cannot\u00a0accept.\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Why three? Because delegates can have multiple subscribers, and one subscriber can overwrite&nbsp;another&#8217;s&nbsp;result. Microsoft Learn&#8217;s&nbsp;EventHandlerResult&nbsp;page describes the problem directly:&nbsp;<em>&#8220;If the response is gathered by using an&nbsp;EventHandlerResult&nbsp;object, the second subscriber that validates and replies with Boolean true might overwrite the Boolean false from the first subscriber.&#8221;<\/em>&nbsp;The accept-only and reject-only types narrow what each subscriber can express, which makes the multi-subscriber case behave more predictably.&nbsp;<\/p>\n\n\n\n<p>There&#8217;s&nbsp;also a stricter mechanism:&nbsp;EventHandlerResult::newSingleResponse(). When the publisher instantiates the result this way, the framework throws an exception as soon as a second subscriber tries to provide a result. This is the right choice when the publisher genuinely needs at most one subscriber to answer \u2014 overlapping ISVs becomes a fail-fast scenario instead of a silent overwrite.&nbsp;<\/p>\n\n\n\n<p><strong>Three legitimate uses of delegates<\/strong>&nbsp;<\/p>\n\n\n\n<p>Delegates are general-purpose, but in practice they get used&nbsp;for&nbsp;three distinct things. Recognizing which one&nbsp;you&#8217;re&nbsp;doing matters because the contract you publish should match the intent.&nbsp;<\/p>\n\n\n\n<p><strong>1.&nbsp;Notify-only&nbsp;\u2014 &#8220;this just happened&#8221;.&nbsp;<\/strong>The publisher fires the delegate to tell anyone listening that something occurred. No response&nbsp;expected. No&nbsp;EventHandlerResult&nbsp;parameter. Subscribers can do whatever they want \u2014 log it, push it elsewhere, ignore it. This is the cleanest contract: the publisher is genuinely indifferent to subscriber&nbsp;behaviour.&nbsp;<\/p>\n\n\n\n<p><strong>2. Request-and-respond \u2014 &#8220;someone please answer this&#8221;.&nbsp;<\/strong>The publisher needs information that subscribers might supply. The delegate signature includes an&nbsp;EventHandlerResult-family parameter. The publisher reads the result after the delegate&nbsp;fires. This is the pattern in Microsoft Learn&#8217;s&nbsp;InventWarehouseEntity&nbsp;validateWarehouseTypeDelegate&nbsp;example. Use this when the answer might depend on logic the publisher&nbsp;can&#8217;t&nbsp;reasonably know.&nbsp;<\/p>\n\n\n\n<p><strong>3. Request-and-reject \u2014 &#8220;please veto this if you have a reason&#8221;.&nbsp;<\/strong>Specialized version of #2 using&nbsp;EventHandlerRejectResult. The publisher proceeds unless a subscriber&nbsp;rejects. Useful for plug-in validation: any subscriber can&nbsp;block,&nbsp;none has to say yes.&nbsp;<\/p>\n\n\n\n<p><strong>The pattern Microsoft tells you not to write<\/strong>&nbsp;<\/p>\n\n\n\n<p>Microsoft Learn&#8217;s&nbsp;EventHandlerResult&nbsp;page has an unusually direct warning, with a code example labelled &#8220;This example is an example of code that you should not write.&#8221; Worth reading in full because&nbsp;it&#8217;s&nbsp;the single most-broken delegate pattern on real projects.&nbsp;<\/p>\n\n\n\n<p><strong>Microsoft&#8217;s exact words:&nbsp;<\/strong><em>&#8220;Before the subscribing logic responds, it should not evaluate whether the result object parameter already contains a result.&#8221;<\/em>&nbsp;<\/p>\n\n\n\n<p>Rephrased:&nbsp;don&#8217;t&nbsp;write a subscriber that does &#8220;if some other subscriber&nbsp;already&nbsp;answered, I&#8217;ll skip&#8221;.&nbsp;Don&#8217;t&nbsp;write one that does &#8220;if no one else&nbsp;answered, I&#8217;ll provide a default&#8221;. Either of those&nbsp;couples&nbsp;your subscriber to the assumed&nbsp;behaviour&nbsp;of other subscribers, which you&nbsp;don&#8217;t&nbsp;control and&nbsp;shouldn&#8217;t&nbsp;reason about.&nbsp;<\/p>\n\n\n\n<p>The correct shape: each subscriber answers if and only if its own conditions are met. Each subscriber&#8217;s logic stays self-contained. The framework \u2014 or the&nbsp;newSingleResponse&nbsp;constructor \u2014 handles what happens when multiple subscribers try to answer.&nbsp;Don&#8217;t&nbsp;write defensive code against the multi-subscriber case in your own subscriber.&nbsp;That&#8217;s&nbsp;the framework&#8217;s responsibility.&nbsp;<\/p>\n\n\n\n<p><strong>When to publish your own delegate<\/strong>&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"1024\" src=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-1024x1024.png\" alt=\"\" class=\"wp-image-1465\" srcset=\"https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-1024x1024.png 1024w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-300x300.png 300w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-150x150.png 150w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-768x768.png 768w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-1170x1170.png 1170w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b-585x585.png 585w, https:\/\/cloudclif.com\/blogs\/wp-content\/uploads\/2026\/04\/d04d72eb-508b-4dea-8516-44396582ce5b.png 1254w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Most discussion of delegates focuses on subscribing \u2014 finding a Microsoft delegate that fires near the point you need to&nbsp;extend, and&nbsp;hooking into it. But the more interesting question is when to publish one in your own code.&nbsp;<\/p>\n\n\n\n<p>Publish a delegate when:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You&#8217;re\u00a0writing code that other extensions will want to plug into. ISV solutions, partner-published frameworks, internal libraries used across multiple projects \u2014 all need explicit extension points.\u00a0<\/li>\n\n\n\n<li>The right extension point is in the middle of a method, not before or after it. CoC and Pre\/Post handlers can only\u00a0wrap\u00a0whole methods; if subscribers need to intervene at a specific point inside the method, a delegate fired from that point is the only clean answer.\u00a0<\/li>\n\n\n\n<li>You want the contract to be discoverable. A published delegate shows up in tooling and code searches as an explicit extension point, more visible than an internal method waiting for someone to\u00a0CoC\u00a0it.\u00a0<\/li>\n\n\n\n<li>You want\u00a0forward-compatibility. Microsoft can change a\u00a0method&#8217;s\u00a0internal logic across versions; a delegate fired from inside that method is a stable contract\u00a0you&#8217;ve\u00a0made with subscribers, decoupled from the implementation around it.\u00a0<\/li>\n\n\n\n<li>Don&#8217;t\u00a0publish a delegate when:\u00a0<\/li>\n\n\n\n<li>There&#8217;s\u00a0already a Microsoft delegate firing close to the same point. Two delegates\u00a0means\u00a0subscribers\u00a0have to\u00a0know which to subscribe to.\u00a0<\/li>\n\n\n\n<li>The right answer is a\u00a0method\u00a0override. If subscribers genuinely need to replace your method&#8217;s\u00a0behaviour, CoC on a public method is the cleaner path.\u00a0<\/li>\n\n\n\n<li>You&#8217;re\u00a0tempted to fire one as a notification just so subscribers can read\u00a0state. Refactor that\u00a0state\u00a0into a public read-only property instead.\u00a0<\/li>\n<\/ul>\n\n\n\n<p><strong>3 tricky interview questions (with verified answers)<\/strong>&nbsp;<\/p>\n\n\n\n<p><strong>Q1. A delegate has the signature delegate void&nbsp;myDelegate(int x,&nbsp;EventHandlerResult&nbsp;result). Two subscribers&nbsp;respond. What value does the publisher see?<\/strong>&nbsp;<\/p>\n\n\n\n<p>Whatever the second subscriber set \u2014 last write wins. Microsoft Learn&#8217;s&nbsp;EventHandlerResult&nbsp;page is explicit:&nbsp;<em>&#8220;the&nbsp;EventHandlerResult&nbsp;class can only contain a single result.&nbsp;If multiple subscribers provide their individual results, the last respondent wins and overwrites the results from the previous subscribers.&#8221;<\/em>&nbsp;Worse, you&nbsp;can&#8217;t&nbsp;predict which subscriber runs second \u2014&nbsp;there&#8217;s&nbsp;no guaranteed sequence.&nbsp;<em>&#8220;There is not a defined order in the processing of handler methods&#8221;<\/em>, per Microsoft&#8217;s delegates-migration reference. The fix is either to design the contract so only one subscriber can&nbsp;plausibly respond&nbsp;(use&nbsp;EventHandlerAcceptResult&nbsp;or&nbsp;EventHandlerRejectResult&nbsp;to constrain the response), or&nbsp;use&nbsp;EventHandlerResult::newSingleResponse()&nbsp;to get a fail-fast exception when more than one subscriber answers.&nbsp;<\/p>\n\n\n\n<p><strong>Q2. Can a delegate have a non-void return type?<\/strong>&nbsp;<\/p>\n\n\n\n<p>No.&nbsp;<em>&#8220;Delegates in X++ must have the return type void&#8221;<\/em>&nbsp;\u2014&nbsp;Docentric&#8217;s&nbsp;delegates and event handlers writeup states this directly, and Microsoft Learn&#8217;s solve-dependencies-with-delegates reference confirms&nbsp;<em>&#8220;due to the fact that delegates do not have a return value, an&nbsp;EventHandlerResult&nbsp;is passed as a parameter to provide access to the needed result value.&#8221;<\/em>&nbsp;A candidate who tries to declare&nbsp;delegate&nbsp;boolean&nbsp;myDelegate(&#8230;)&nbsp;is showing they haven&#8217;t actually written a delegate before. The right pattern is the&nbsp;EventHandlerResult&nbsp;parameter.&nbsp;<\/p>\n\n\n\n<p><strong>Q3. Should a delegate handler check whether&nbsp;result.result() is already populated before responding?<\/strong>&nbsp;<\/p>\n\n\n\n<p>No, and Microsoft Learn explicitly labels this as bad practice. The exact words:&nbsp;<em>&#8220;Before the subscribing logic responds, it should not evaluate whether the result object parameter already contains a result.&#8221;<\/em>&nbsp;Each subscriber should answer based on its own conditions, independently of whether other subscribers have answered. Coupling your subscriber to other subscribers&#8217;&nbsp;behaviour&nbsp;is a fragile dependency you&nbsp;don&#8217;t&nbsp;control. The framework manages multi-subscriber&nbsp;contention&nbsp;via&nbsp;EventHandlerAcceptResult,&nbsp;EventHandlerRejectResult, or&nbsp;newSingleResponse. A senior candidate adds: if a delegate handler in production code is doing this check,&nbsp;it&#8217;s&nbsp;a signal the contract is ill-defined and should be redesigned, not patched.&nbsp;<\/p>\n\n\n\n<p><strong>What to&nbsp;actually do<\/strong>&nbsp;<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Before\u00a0reaching for\u00a0CoC on an internal Microsoft method, search the area for an existing delegate. Microsoft has published many in posting, validation, and pricing flows specifically to provide cleaner extension points. Right-click a class name in Visual Studio and use &#8220;Find references&#8221; to surface delegates and\u00a0SubscribesTo\u00a0handlers in that area.\u00a0<\/li>\n<\/ol>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>When you do subscribe to a delegate, write each handler self-contained.\u00a0Don&#8217;t\u00a0check whether other subscribers\u00a0already\u00a0answered.\u00a0Don&#8217;t\u00a0provide a default for the multi-subscriber case. Each handler responds based on its own conditions only.\u00a0<\/li>\n<\/ol>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>When the contract\u00a0genuinely needs\u00a0at most one subscriber, use\u00a0EventHandlerResult::newSingleResponse() in the publisher. Fail-fast on\u00a0contention beats silent overwrites.\u00a0<\/li>\n<\/ol>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li>When publishing your own delegates in framework or ISV code, prefer\u00a0EventHandlerAcceptResult\u00a0or\u00a0EventHandlerRejectResult\u00a0over generic\u00a0EventHandlerResult\u00a0when the response is binary. The narrower type prevents whole classes of multi-subscriber confusion.\u00a0<\/li>\n<\/ol>\n\n\n\n<ol start=\"5\" class=\"wp-block-list\">\n<li>Treat published delegates as part of your public API. Renaming, signature changes, or removal will\u00a0break\u00a0every subscriber. The same compatibility discipline\u00a0you&#8217;d\u00a0apply to a public class method applies.\u00a0<\/li>\n<\/ol>\n\n\n\n<p><strong>References<\/strong>&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Microsoft Learn<\/strong>\u00a0\u2014 Events and delegates (learn.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-ref\/xpp-events). Authoritative reference for delegate syntax, the\u00a0SubscribesTo\u00a0attribute, and the\u00a0FMRentalCheckoutProcessor\u00a0example used above. Last updated February 2025.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Microsoft Learn<\/strong>\u00a0\u2014 Respond by using\u00a0EventHandlerResult\u00a0(learn.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/extensibility\/respond-event-handler-result). Source for the explicit &#8220;do not write this&#8221; warning and the\u00a0InventWarehouseEntity\u00a0validateWarehouseTypeDelegate\u00a0example. Last updated May 2025.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Microsoft Learn<\/strong>\u00a0\u2014\u00a0EventHandlerResult\u00a0classes in request or response scenarios (learn.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-tools\/event-handler-result-class). Source for\u00a0EventHandlerAcceptResult,\u00a0EventHandlerRejectResult, and\u00a0newSingleResponse. Documents the last-write-wins\u00a0behaviour.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Microsoft Learn<\/strong>\u00a0\u2014 Solve dependencies among models by using delegates during code migration (learn.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/migration-upgrade\/delegates-migration). Source for the void-return-type rule and the no-defined-order statement on subscriber processing.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Docentric<\/strong>\u00a0\u2014 Delegates and Event Handlers in D365FO (ax.docentric.com\/delegates-and-event-handlers-in-d365fo\/). Best community-level explanation of when to use delegates vs other extension\u00a0mechanisms;\u00a0covers\u00a0EventHandlerResult\u00a0patterns with current syntax. Updated\u00a0February\u00a02026.\u00a0<\/li>\n<\/ul>\n\n\n\n<p><em>Next up in this series: Best Practice Checks \u2014 Fighting the Warnings You&#8217;ll Actually See in Every Build.&nbsp;[PASTE DAY 7 URL HERE]<\/em>&nbsp;<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reading time: ~8 minutes.&nbsp; Delegates are the cleanest extension mechanism in X++. They are also the least taught. Most F&amp;O developers reach for CoC or event handlers first, write working&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[35],"tags":[],"class_list":["post-1450","post","type-post","status-publish","format-standard","hentry","category-d365-fo"],"_links":{"self":[{"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/posts\/1450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/comments?post=1450"}],"version-history":[{"count":9,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/posts\/1450\/revisions"}],"predecessor-version":[{"id":1467,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/posts\/1450\/revisions\/1467"}],"wp:attachment":[{"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/media?parent=1450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/categories?post=1450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudclif.com\/blogs\/wp-json\/wp\/v2\/tags?post=1450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}