IncaTechnologies.ObjectsMessenger
1.0.0-beta
Prefix Reserved
dotnet add package IncaTechnologies.ObjectsMessenger --version 1.0.0-beta
NuGet\Install-Package IncaTechnologies.ObjectsMessenger -Version 1.0.0-beta
<PackageReference Include="IncaTechnologies.ObjectsMessenger" Version="1.0.0-beta" />
paket add IncaTechnologies.ObjectsMessenger --version 1.0.0-beta
#r "nuget: IncaTechnologies.ObjectsMessenger, 1.0.0-beta"
// Install IncaTechnologies.ObjectsMessenger as a Cake Addin #addin nuget:?package=IncaTechnologies.ObjectsMessenger&version=1.0.0-beta&prerelease // Install IncaTechnologies.ObjectsMessenger as a Cake Tool #tool nuget:?package=IncaTechnologies.ObjectsMessenger&version=1.0.0-beta&prerelease
ObjectsMessenger
Utility class to provide objects communication.
What it should do?
- Allow communication between objects.
- Enforce the existence of a message channel.
- Ensure that only senders can send messages, and only receivers can receive them.
- Support dependency injection.
- Support asyncronous communication.
- Define communication in a self-contained object.
- Expose events related to the communication process.
- Optionally deliver messages to anyone.
- Not bind the messaging process to properties.
- Provide an option to persist or delete messages after delivery.
- Not cause memory leaks due to sender storage.
- Only interfaces should be used to interact with the system.
- Include a utility for dealing with property accessors.
- Not rely on exceptions.
- Rely on observables instead of events.
How it is done?
Implement Messenger<TSender, TReceiver, TMessage>
to create a single-channel communication between two objects. Otherwise, implement Messenger<TSender, TReceiver>
to establish multichannel communication, where one object can dispatch messages to anyone.
Optionally, register the messengers in the MessengerHub
to handle all messenger events and errors.
Implement a single channel messenger
Given a sender class like:
public sealed class UsersViewModel
{
private User? _currentUser;
public User? CurrentUser
{
get => _currentUser;
set => _currentUser = value;
}
public IEnumerable<User> Users { get; }
public UsersViewModel(...) {...}
}
And a receiver class like:
public sealed class EditUserViewModel
{
public User? User { get; set; }
}
Let's assume that you want to deliver the current user from UsersViewModel
to EditUserViewModel
. Then, implement a single-channel messenger like:
public sealed class CurrentUserMessenger : Messenger<UsersViewModel, EditUserViewModel, User?>
{
public override bool IsMessagePreserved => false;
protected override void ReceiveMessage(EditUserViewModel receiver, User? message) => receiver.User = message;
protected override User? SendMessage(UsersViewModel sender) => sender.CurrentUser;
}
Refactor UsersViewModel
and EditUserViewModel
to send and receive the message as appropriate. What follows is just an example of how the communication process might be arranged.
public sealed class UsersViewModel
{
private readonly CurrentUserMessenger _currentUserMessenger;
private User? _currentUser;
public User? CurrentUser
{
get => _currentUser;
set
{
_currentUser = value;
_currentUserMessenger.Send(this);
}
}
public IEnumerable<User> Users { get; }
public UsersViewModel(...) {...}
}
public sealed class EditUserViewModel
{
public User? User { get; set; }
public EditUserViewModel(CurrentUserMessenger currentUserMessenger)
{
currentUserMessenger.Receive(this);
}
}
Implement a multichannel messenger
Implementing a multi-channel Messenger
does not differ much from the single channel. First, implement Messenger<TSender, TMessage>
.
public sealed class CurrentUSerPublisher : Messenger<UsersViewModel, User?>
{
public override User? Default => default;
protected override User? SendMessage(UsersViewModel sender) => sender.CurrentUser;
}
The sending process remains the same, whereas the receiving process lets you get the message directly. Like so:
public sealed class EditUserViewModel
{
public User? User { get; set; }
public EditUserViewModel(CurrentUserMessenger currentUserMessenger)
{
User = currentUserMessenger.Receive();
}
}
Register the messenger
It is possible to register any Messenger
into the MessengerHub
. This will allow handling the messenger's events in a single place.
MessengerHub.Default.RegisterMessenger(currentUserMessenger);
Doing this will allow the creation of behaviors. For example, the EditUserViewModel
from before can be refactored to better separate concerns.
public sealed class ReceiveCurrentUserBehavior
{
public ReceiveCurrentUserBehavior(CurrentUserMessenger currentUserMessenger, EditUserViewModel editUserViewModel)
{
currentUserMessenger.Events
.Where(@event => @event is MessengerEvent.Sended)
.Subscribe(_ => currentUserMessenger.Receive(editUserViewModel));
}
}
Notes
The use of IObservable
instead of CLR events makes the system very flexible. For more information, check System.Reactive.
Utilities
Dependency injection
services.RegisterMessengers(Assembly.GetExecutingAssembly());
This call not only registers all Messenger
instances in the given assembly, but also registers all the Messenger
instances in the MessengerHub
.
Property setter
The SetAndSend(...)
function can be used to simplify the sending process for a property.
public sealed class UsersViewModel
{
private readonly CurrentUserMessenger _currentUserMessenger;
private User? _currentUser;
public User? CurrentUser
{
get => _currentUser;
set => _currentUserMessenger.SetAndSend(this, ref _currentUser, value);
}
public IEnumerable<User> Users { get; }
public UsersViewModel(...) {...}
}
Contribute
Do you like this library, and do you want to make it better? Just open an issue on GitHub.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.1)
- System.Reactive (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.0-beta | 22 | 5/2/2024 |
1.0.0-beta : Release