Disposable pattern (
IDisposable interface) suggests the possibility of releasing some of the resources occupied by an object by calling the
Dispose method even before all references to the instance are lost and the garbage collector recycles it (although for reliability, the
Dispose call is often duplicated in the finalizer).
But there is also a reverse
Exposable pattern, when a reference to an object becomes available until it is fully initialized. That is, the instance is already present in memory, partially initialized, and other objects refer to it, but to finally prepare it for work, you need to call the
Expose method. Again, this call can be performed in the constructor, which is diametrically called
Dispose in the finalizer.
In itself, the presence of such a return symmetry looks beautiful and natural, but where it can be useful in practice, we will try to reveal in this article.
')

For reference, in
C # there is a
using directive - syntactic sugar for safely calling the
Dispose method.
using(var context = new Context()) {
is equivalent to
var context = new Context(); try {
the only difference is that in the first case the
context variable becomes
read-only .
Unit of Work + Disposable + Exposable = Renewable UnitDispose of the pattern often accompanies the
Unit of Work pattern when objects are designed for one-time use, and their lifetimes are usually short. That is, they are created, immediately used, and then immediately release the occupied resources, becoming unsuitable for further use.
For example, such a mechanism is often used to access database entities through ORM frameworks.
using(var context = new DbContext(ConnectionString)) { persons =context.Persons.Where(p=>p.Age > minAge).ToList(); }
The connection to the database is opened, the necessary request is made, and then it is immediately closed. Keeping a connection permanently open is considered a bad practice, since often the resource of connections is limited, and connections are automatically closed after a certain period of inactivity.
Everything is good, but if we have a server with an uneven load, then during peak hours, user requests will create huge numbers of such instances of
DbContext objects, which will have an effect on the memory consumed by the server and speed, as the garbage collector will be called more often.
Here, sharing the
Disposable and
Exposable patterns can help. Instead of constantly creating and deleting objects, it is enough to create one object, and then in it to occupy and release resources.
context.Expose(); persons = context.Persons.Where(p=>p.Age > minAge).ToList(); context.Dispose();
Of course, this code will not work with existing frameworks, since the
Expose method is not provided in them, but it is important to show the principle itself - objects can be reused, and the necessary resources can be resumed dynamically.
* As noted in the comment, perhaps this is not the most successful example, since the performance gain is quite controversial. But in order to better grasp the essence of the pattern in question, we present the following reasoning.In the usual sense of
Disposable - deinitialization and complete abandonment of the object. However, a reference to it may well remain after calling
Dispose . Often, accessing most properties and methods will cause an exception if the programmer has provided this, but the instance can usually be easily used as a key, called
ToString ,
Equals, and some other methods. So why not expand the understanding of the
Disposable pattern? Let
Dispose put an object into a state of alert, when it takes up less resources, to sleep! But then there must be a method out of this state -
Expose . Everything is very logical and logical. That is, we have received some generalization of the
Disposable pattern, and the scenario of abandoning an object is only its special case.
Independent Injections via Exposable PatternImportant! For a complete understanding of the above, it is highly recommended to download the
source code (
backup link ) of the
Aero Framework library with the example of the text editor
Sparrow , and also it is advisable to get acquainted with the series of previous articles.
Binding and xaml markup extensions using localization as an exampleXaml context injectorsCommand-Oriented Navigation in Xaml ApplicationsImproving xaml: Bindable Converters, Switch Converter, SetsSugar injections in C #Context Model Pattern via Aero FrameworkThe classic way of injecting view models into a constructor using
unit containers is as follows:
public class ProductsViewModel : BaseViewModel { public virtual void ProductsViewModel(SettingsViewModel settingsViewModel) {
But such code will cause an exception, since it is impossible to initialize the
ProductsViewModel until the
SettingsViewModel has been created and vice versa.
However, using the
Exposable pattern in the
Aero Framework library allows you to elegantly solve the problem of closed dependencies:
public class ProductsViewModel : ContextObject, IExposable { public virtual void Expose() { var settingsViewModel = Store.Get<SettingsViewModel>(); this[Context.Get("AnyCommand")].Executed += (sender, args) => {
Together with the state-saving mechanism (
Smart State , which is just below), this makes it possible to safely initialize both view models that reference each other, that is, to implement the
principle of independent direct injections .
Smart StateNow we come to a very unusual, but at the same time useful mechanism for maintaining state.
Aero Framework allows you to very elegantly and unmatchedly concisely solve problems of this kind.
Run the desktop version of the
Sparrow editor, which is an example library application. This is a normal window that can be dragged or resized (visual state). You can also create multiple tabs or open text files, and then edit the text in them (logical state).
After that, close the editor (click the cross on the window) and run it again. The program will start exactly in the same visual and logical state in which it was closed, that is, the size and position of the window will be the same, the working tabs will remain open and even the text in them will be the same as it was left when closed! Meanwhile, the source codes of the view models at first glance do not contain any auxiliary logic for saving the state, how did it happen?
Upon careful consideration, you may notice that the view models in the
Sparrow sample application are marked with the
DataContract attribute, and some properties with the
DataMember attribute , which allows you to use serialization and deserialization mechanisms to preserve and restore the logical state.
All you need to do to do this is to initialize the necessary framework during the launch of the application:
Unity.AppStorage = new AppStorage(); Unity.App = new AppAssistent();
By default, serialization occurs in files, but you can easily create your own implementation and save serialized objects, for example, to a database. For this you need to inherit from the
Unity.IApplication interface (
AppStorage is implemented by
default ). As for the interface
Unity.IApplication (
AppAssistent ), it is necessary for cultural settings during serialization and in most cases it can be limited to its standard implementation.
To save the state of any object that supports serialization, simply call the Snapshot attachment method, or use the
Store.Snapshot call if the object is in the general container.
We have dealt with the preservation of the logical state, but it often becomes necessary to store and visually, for example, the size and position of the windows, the state of the controls and other parameters. The framework offers a custom, but incredibly convenient solution. What if you store such parameters in context objects (view models), but not as separate properties for serialization, but implicitly, as a dictionary, where the key is the name of the “imaginary” property?
Based on this concept, the idea of
smart properties was born. The value of the
smart property must be accessible through the indexer by the key name, as in a dictionary, and the classic
get or
set are optional and may be missing! This functionality is implemented in the
SmartObject class, from which
ContextObject inherits, extending it.
Just write in the desktop version:
public class AppViewModel : SmartObject
<Window x:Class="Sparrow.Views.AppView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="clr-namespace:Sparrow.ViewModels" DataContext="{Store Key=viewModels:AppViewModel}" WindowStyle="{ViewModel DefaultValue=SingleBorderWindow}" ResizeMode="{Binding '[ResizeMode, CanResizeWithGrip]', Mode=TwoWay}" Height="{Binding '[Height, 600]', Mode=TwoWay}" Width="{ViewModel DefaultValue=800}" Left="{ViewModel DefaultValue=NaN}" Top="{Binding '[Top, NaN]', Mode=TwoWay}" Title="{ViewModel DefaultValue='Sparrow'}" Icon="/Sparrow.png" ShowActivated="True" Name="This"/>
after which the size and position of the window will be automatically saved when you exit the application and are exactly restored at startup! Agree, this is an amazing conciseness for solving this kind of problem. I did not have to write a single extra line of code in the view model or code-behaine.
* For small nuances and limitations of some other
xaml platforms, as well as workarounds, see the original article
Context Model Pattern via Aero Framework .
Thanks to the polymorphism mechanism, the validation of property values ​​using the implementation of the
IDataErrorInfo interface, which also uses an indexer, fits neatly into the concept of smart state.
ResultsIt may seem that we have deviated from the main theme, but this is not so. Everything described in this and previous articles, mechanisms together using the
Exposable pattern
, allow you to create very clean and concise view-models.
public class HelloViewModel : ContextObject, IExposable { public string Message { get { return Get(() => Message); } set { Set(() => Message, value); } } public virtual void Expose() { this[() => Message].PropertyChanged += (sender, args) => Context.Make.RaiseCanExecuteChanged(); this[Context.Show].CanExecute += (sender, args) => args.CanExecute = !string.IsNullOrEmpty(Message); this[Context.Show].Executed += async (sender, args) => { await MessageService.ShowAsync(Message); }; } }
That is, it can easily happen that several properties and only one
Expose method are declared in the view model, and the rest of the functional is described by lambda expressions! And if further inheritance is planned, then you should simply mark the method with the
virtual modifier.