Attributes that make methods extensible
This article describes the various attributes that can be used to control extensibility capabilities for methods.
The following table provides an overview of the default support for extensibility and accessibility on methods. The table also provides guidance on the method signature changes.
Attribute | Hookable | Wrappable | Replaceable | Accessibility | Signature |
---|---|---|---|---|---|
private | No | N/A | N/A | Accessible from within class it is defined in. | Signature can be changed |
protected internal | No | Yes, from Platform update 25 onward | No | Accessible from with the class it is defined, from derived classes, and from classes in the same model. | Signature must remain compatible |
internal | No | No | No | Accessible in the same model. | Signature can be changed |
protected | No | Yes, unless marked final | No | Accessible from with the class it is defined and from derived classes. | Signature must remain compatible |
public | Yes | Yes, unless marked final | No | Accessible from within the class it is defined, derived classes, and other classes that have access to the defining class. | Signature must remain compatible |
Hookable
If a method is hookable, extenders can subscribe to pre-events and post-events.
For public methods, you can opt out by adding [Hookable(false)] to the method.
You can opt in for private and protected methods by adding [Hookable(true)] to the method.
If a method is explicitly marked as [Hookable(false)], then it is not wrappable.
Best practices when you write code
When a method is hookable, the compiler generates extra intermediate language (IL) code to enable the method as an extension point. Although the extra code has performance overhead, this overhead is negligible in most cases. However, for performance-critical methods, consider marking the method as non-hookable.
Wrappable
If a method is wrappable, extenders can wrap it by using Chain of Command (CoC). Extenders must call next, because they aren't allowed to break the CoC.
For protected and public methods, you can opt out by adding [Wrappable(false)] to the method.
You can't opt in for private methods.
Best practices when you write code
CoC resembles inheritance in many ways. Typically, if you want other people to be able to call your method but not change it, you mark the method as final. Consider marking these methods as non-wrappable or non-hookable.
Replaceable
If a method is replaceable, extenders can wrap it by using CoC, but they don't have to unconditionally call next. Although extenders can break the CoC, the expectation is that they will only conditionally break it. The compiler doesn't enforce calls to next.
To be replaceable, a method must also be wrappable.
For wrappable methods, you can opt in by adding [Replaceable] to the method.
Best practices when you write code
When a method is replaceable, it can be extended by using CoC, and the execution of next can be skipped. Before you enable a method to be replaceable, you should thoroughly assess the functional impact if an extender skips the execution of the method.
Do make sure that methods that have [Replaceable] have XML documentation that describes the responsibility of the method.
Don't use [Replaceable] to let consumers skip the replaced logic and do nothing.
Don't use [Replaceable] for factory methods when SysExtension can be used instead.
Avoid using [Replaceable] when the method changes databases or class state.
Avoid using [Replaceable] if the method performs multiple operations and has multiple responsibilities. Instead, refactor the method into separate methods, each of which has a single responsibility, and consider which methods should actually be replaceable.
Consider using [Replaceable] to solve transformations.
Example: Enum conversion that uses a switch statement over enum values, where the default block has a throw.
Consider using [Replaceable] to override lookups and jumprefs.
Best practices for extenders
- Don't write logic that has a different responsibility than the logic that is being replaced.
- Do call the base functionality (call next) when the replacement logic doesn't apply.
- Avoid replacing logic completely by not calling the base functionality (call next).
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for