-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
494 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/[Bb]in/ | ||
/[Oo]bj/ | ||
/[Bb]uild/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": ".NET Core Launch (console)", | ||
"type": "coreclr", | ||
"request": "launch", | ||
"preLaunchTask": "build", | ||
"program": "${workspaceFolder}/bin/Debug/net6.0/MessageBus.exe", | ||
"args": [], | ||
"cwd": "${workspaceFolder}", | ||
"stopAtEntry": false, | ||
"console": "internalConsole" | ||
}, | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"dotnet.defaultSolution": "MessageBus.sln" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"tasks": [ | ||
{ | ||
"label": "build", | ||
"command": "dotnet", | ||
"type": "process", | ||
"args": [ | ||
"build", | ||
"${workspaceFolder}/MessageBus.sln" | ||
], | ||
"problemMatcher": "$msCompile" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//------------------------------------------------------------------------------ | ||
// MIT License | ||
// | ||
// Copyright (c) 2024 Tobias Barendt | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
//------------------------------------------------------------------------------ | ||
|
||
public class Example | ||
{ | ||
//-------------------------------------------------------------------------- | ||
// Messages | ||
public delegate void ExampleMessage(); | ||
public delegate void ExampleArguments(int x, int y); | ||
|
||
//-------------------------------------------------------------------------- | ||
// Constructor | ||
//-------------------------------------------------------------------------- | ||
public Example() | ||
{ | ||
// Example of how to use it unscoped | ||
UnscopedExample(); | ||
|
||
// Example on how to use it with a scope | ||
ScopedExample(); | ||
|
||
|
||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// UnscopedExample | ||
//-------------------------------------------------------------------------- | ||
private void UnscopedExample() | ||
{ | ||
//---------------------------------------------------------------------- | ||
// Subscribe to a few messages | ||
MessageBus.Subscribe<ExampleMessage>(OnExampleMessage); | ||
MessageBus.Subscribe<ExampleArguments>(OnExampleArguments); | ||
|
||
|
||
//---------------------------------------------------------------------- | ||
// Dispatch messages | ||
MessageBus.Dispatch<ExampleMessage>(); | ||
MessageBus.Dispatch<ExampleArguments>(100, 200); | ||
|
||
//---------------------------------------------------------------------- | ||
// Remove subscriptions | ||
MessageBus.Unsubscribe<ExampleMessage>(OnExampleMessage); | ||
MessageBus.Unsubscribe<ExampleArguments>(OnExampleArguments); | ||
} | ||
|
||
|
||
//-------------------------------------------------------------------------- | ||
// ScopedExample | ||
//-------------------------------------------------------------------------- | ||
private void ScopedExample() | ||
{ | ||
//---------------------------------------------------------------------- | ||
// Subscribe to a few messages | ||
MessageBus.Subscribe<ExampleMessage>("ExampleScope", OnExampleMessage); | ||
MessageBus.Subscribe<ExampleArguments>("ExampleScope", OnExampleArguments); | ||
|
||
|
||
//---------------------------------------------------------------------- | ||
// Dispatch messages | ||
var scopedDispatcher = MessageBus.GetDispatcher("ExampleScope"); | ||
|
||
scopedDispatcher.Dispatch<ExampleMessage>(); | ||
scopedDispatcher.Dispatch<ExampleArguments>(64, 128); | ||
|
||
//---------------------------------------------------------------------- | ||
// Remove subscriptions | ||
MessageBus.Unsubscribe<ExampleMessage>("ExampleScope", OnExampleMessage); | ||
MessageBus.Unsubscribe<ExampleArguments>("ExampleScope", OnExampleArguments); | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// OnExampleMessage | ||
//-------------------------------------------------------------------------- | ||
private void OnExampleMessage() | ||
{ | ||
Console.WriteLine("OnExampleMessage"); | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// OnExampleArguments | ||
//-------------------------------------------------------------------------- | ||
private void OnExampleArguments(int x, int y) | ||
{ | ||
Console.WriteLine("ExampleArguments with " + x + " and " + y); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.30114.105 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessageBus", "MessageBus.csproj", "{AC802790-5432-4DFD-B9E0-FB66DA790C92}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{AC802790-5432-4DFD-B9E0-FB66DA790C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{AC802790-5432-4DFD-B9E0-FB66DA790C92}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{AC802790-5432-4DFD-B9E0-FB66DA790C92}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{AC802790-5432-4DFD-B9E0-FB66DA790C92}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
//------------------------------------------------------------------------------ | ||
// MIT License | ||
// | ||
// Copyright (c) 2024 Tobias Barendt | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
//------------------------------------------------------------------------------ | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
|
||
//------------------------------------------------------------------------------ | ||
// MessageBus | ||
// | ||
// The message bus is a central point for communication between different parts | ||
// of the application. It is used to decouple different parts of the application | ||
// and to allow for a more modular design. | ||
// | ||
public class MessageBus | ||
{ | ||
//-------------------------------------------------------------------------- | ||
// Singleton instance | ||
private static MessageBus instance = new MessageBus(); | ||
|
||
//-------------------------------------------------------------------------- | ||
// Message Scopes | ||
private Dictionary<string, MessageDispatcher> m_scopes = new(); | ||
|
||
//-------------------------------------------------------------------------- | ||
// GetDispatcher | ||
// | ||
// Returns the message dispatcher for the given scope. IF scoped does not | ||
// already exist it will be created. | ||
// | ||
//-------------------------------------------------------------------------- | ||
public static MessageDispatcher GetDispatcher() => instance.Internal_GetDispatcher(""); | ||
public static MessageDispatcher GetDispatcher(string scope) => instance.Internal_GetDispatcher(scope); | ||
private MessageDispatcher Internal_GetDispatcher(string scope) | ||
{ | ||
if(m_scopes.TryGetValue(scope, out var dispatcher)) | ||
return dispatcher; | ||
|
||
m_scopes[scope] = new MessageDispatcher(scope); | ||
return m_scopes[scope]; | ||
} | ||
//-------------------------------------------------------------------------- | ||
// Dispatch | ||
// | ||
// Unscoped dispatch, will dispatch to all unscoped subscribers | ||
// Use GetDispatcher for scoped dispatching | ||
// | ||
//-------------------------------------------------------------------------- | ||
public static void Dispatch<MessageType>(params object[] args) where MessageType : System.Delegate => instance.Internal_DispatchEvent(typeof(MessageType).GetHashCode(), args); | ||
public static void Dispatch(System.Type messageType, params object[] args) => instance.Internal_DispatchEvent(messageType.GetHashCode(), args); | ||
private void Internal_DispatchEvent(int type, params object[] args) | ||
{ | ||
if(!m_scopes.ContainsKey(""))Internal_GetDispatcher(""); | ||
m_scopes[""].Dispatch(type, args); | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// Subscribe | ||
//-------------------------------------------------------------------------- | ||
public static void Subscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate => instance.Internal_Subscribe("", subscriber); | ||
public static void Subscribe<MessageType>(string scope, MessageType subscriber) where MessageType : System.Delegate => instance.Internal_Subscribe(scope, subscriber); | ||
private void Internal_Subscribe<MessageType>(string scope, MessageType subscriber) where MessageType : System.Delegate | ||
{ | ||
if(!m_scopes.ContainsKey(scope))Internal_GetDispatcher(scope); | ||
m_scopes[scope].Subscribe(subscriber); | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// Unsubscribe | ||
//-------------------------------------------------------------------------- | ||
public static void Unsubscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate => instance.Internal_Unsubscribe("", subscriber); | ||
public static void Unsubscribe<MessageType>(string scope, MessageType subscriber) where MessageType : System.Delegate => instance.Internal_Unsubscribe(scope, subscriber); | ||
private void Internal_Unsubscribe<MessageType>(string scope, MessageType subscriber) where MessageType : System.Delegate | ||
{ | ||
if(!m_scopes.ContainsKey(scope))return ; | ||
m_scopes[scope].Unsubscribe(subscriber); | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
// IMessageDispatcher | ||
// | ||
// Interface for the message dispatcher, handy for making proxy dispatchers. | ||
// | ||
//------------------------------------------------------------------------------ | ||
public interface IMessageDispatcher | ||
{ | ||
public void Dispatch<MessageType>(params object[] args) where MessageType : System.Delegate; | ||
public void Dispatch(System.Type messageType, params object[] args); | ||
public void Subscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate; | ||
public void Unsubscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate; | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
// MessageDispatcher | ||
// | ||
// The message dispatcher is used to dispatch messages to the correct handlers. | ||
// | ||
//------------------------------------------------------------------------------ | ||
public class MessageDispatcher : IMessageDispatcher | ||
{ | ||
//-------------------------------------------------------------------------- | ||
// Properties | ||
public string Scope {get; private set;} | ||
|
||
//-------------------------------------------------------------------------- | ||
// Subscribers | ||
private Dictionary<int, HashSet<System.Delegate>> m_subscribers = new(); | ||
|
||
//-------------------------------------------------------------------------- | ||
// Cache | ||
private List<System.Delegate> m_delegateList = new(8); | ||
|
||
//-------------------------------------------------------------------------- | ||
// Constructor | ||
//-------------------------------------------------------------------------- | ||
public MessageDispatcher(string scope) | ||
{ | ||
Scope = scope; | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// Subscribe | ||
//-------------------------------------------------------------------------- | ||
public void Subscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate | ||
{ | ||
// Find message type set | ||
int type = subscriber.GetType().GetHashCode(); | ||
if(!m_subscribers.TryGetValue(type, out var messageSet)) | ||
{ | ||
// First time we see this message type, create a new set | ||
messageSet = new HashSet<System.Delegate>(); | ||
m_subscribers[type] = messageSet; | ||
} | ||
|
||
// Add listener | ||
Debug.Assert(!messageSet.Contains(subscriber as System.Delegate), "Listener added twice! [" + subscriber.GetType() + "]"); | ||
messageSet.Add(subscriber as System.Delegate); | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// Unsubscribe | ||
//-------------------------------------------------------------------------- | ||
public void Unsubscribe<MessageType>(MessageType subscriber) where MessageType : System.Delegate | ||
{ | ||
// Find message type set | ||
int type = subscriber.GetType().GetHashCode(); | ||
if(m_subscribers.TryGetValue(type, out var messageSet)) | ||
{ | ||
messageSet.Remove(subscriber as System.Delegate); | ||
if(messageSet.Count == 0) | ||
m_subscribers.Remove(type); | ||
} | ||
} | ||
|
||
//-------------------------------------------------------------------------- | ||
// DispatchEvent | ||
//-------------------------------------------------------------------------- | ||
public void Dispatch<MessageType>(params object[] args) where MessageType : System.Delegate => Internal_DispatchEvent(typeof(MessageType).GetHashCode(), args); | ||
public void Dispatch(System.Type messageType, params object[] args) => Internal_DispatchEvent(messageType.GetHashCode(), args); | ||
public void Dispatch(int type, params object[] args) => Internal_DispatchEvent(type, args); | ||
private void Internal_DispatchEvent(int type, params object[] args) | ||
{ | ||
if(m_subscribers.TryGetValue(type, out var messageSet)) | ||
{ | ||
if(messageSet.Count > 0) | ||
{ | ||
m_delegateList.Clear(); | ||
foreach(System.Delegate dg in messageSet)m_delegateList.Add(dg); | ||
|
||
for(int i = 0; i < m_delegateList.Count; i++) | ||
{ | ||
try | ||
{ | ||
m_delegateList[i].Method.Invoke(m_delegateList[i].Target, args); | ||
} | ||
catch(System.Exception e) | ||
{ | ||
Debug.Assert(false, "Error dispatching message: " + e.Message); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.