Skip to content

Instantly share code, notes, and snippets.

@audinue
Last active July 28, 2017 02:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save audinue/205973bf8f0cad2afc178e0c96db2534 to your computer and use it in GitHub Desktop.
Save audinue/205973bf8f0cad2afc178e0c96db2534 to your computer and use it in GitHub Desktop.
Powerful message subscription system.
/// <summary>
/// Represents an abstract generic message.
/// </summary>
public abstract class Message<T> {
private readonly T content;
public Message(T content) {
this.content = content;
}
public T Content {
get { return content; }
}
}
/// <summary>
/// Represents a generic message subscriber that accepts a message of type T.
/// </summary>
public interface ISubscriber<T> where T : class {
void Receive(object publisher, T message);
}
/// <summary>
/// Represents a message publisher.
/// </summary>
public class Publisher {
private readonly Dictionary<Type, List<object>> subscriptions = new Dictionary<Type, List<object>>();
/// <summary>
/// Initializes a new instance of Publisher class.
/// </summary>
public Publisher() {
}
private void Subscribe(Type type, object subscriber) {
List<object> subscription;
if (!subscriptions.TryGetValue(type, out subscription)) {
subscription = subscriptions[type] = new List<object>();
}
subscription.Add(subscriber);
}
/// <summary>
/// Subscribes a subscriber to a type of message.
/// </summary>
public void Subscribe<T>(ISubscriber<T> subscriber) where T : class {
if (subscriber == null) {
throw new ArgumentNullException("subscriber");
}
Subscribe(typeof(T), subscriber);
}
/// <summary>
/// Subscribes a subscriber to all types of message acceptable by the subscriber.
/// </summary>
public void Subscribe(object subscriber) {
if (subscriber == null) {
throw new ArgumentNullException("subscriber");
}
var types = subscriber
.GetType()
.GetInterfaces()
.Where(type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ISubscriber<>));
if (!types.Any()) {
throw new ArgumentException("Subscriber does not accept any messages types.", "subscriber");
}
foreach (var type in types) {
Subscribe(type.GenericTypeArguments[0], subscriber);
}
}
/// <summary>
/// Unsubscribes a subscriber from a type of message.
/// </summary>
public void Unsubscribe<T>(ISubscriber<T> subscriber) where T : class {
if (subscriber == null) {
throw new ArgumentNullException("subscriber");
}
List<object> subscription;
if (!subscriptions.TryGetValue(typeof(T), out subscription)) {
return;
}
subscription.Remove(subscriber);
}
/// <summary>
/// Unsubscribes a subscriber from all types of message.
/// </summary>
public void Unsubscribe(object subscriber) {
if (subscriber == null) {
throw new ArgumentNullException("subscriber");
}
foreach (var subscription in subscriptions.Values) {
subscription.Remove(subscriber);
}
}
/// <summary>
/// Unsubscribes all subscribers.
/// </summary>
public void Unsubscribe() {
subscriptions.Clear();
}
/// <summary>
/// Publishes a message of type T.
/// </summary>
public void Publish<T>(T message) where T : class {
if (message == null) {
throw new ArgumentNullException("message");
}
List<object> subscription;
if (!subscriptions.TryGetValue(typeof(T), out subscription)) {
return;
}
foreach (ISubscriber<T> subscriber in subscription.ToArray()) {
subscriber.Receive(this, message);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment