how can i implement this situation in RX without using subject. I've read a lot, and I just can't seem to figure it out
public class Member
{
public int Id { get; private set; }
public string Email { get; private set; }
public Member(string email)
{
this.Email = email;
}
}
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
memberAdded.OnNext(member);
}
private Subject<Member> memberAdded = new Subject<Member>();
public IObservable<Member> MemberAdded { get { return memberAdded.AsObservable(); } }
}
public class MemberController
{
public void Create(Member item)
{
var repository = new MemberRepository();
var subs = repository.MemberAdded.Subscribe(x => SendMail(x));
repository.AddMember(item);
}
private void SendMail(Member value)
{
// send welcome mail
}
}
I've don't know how to initialize the IObservable MemberAdded because it is always null if it doesn't have the Subject backer nor do I know how to later call the OnNext at from a later function.
Lastly, is it a problem to have the observables as static properties and all the subscription code in one place?
The way I have implemented something similar is to expose a normal C# event MemberAdded on my MemberRepository. You can then use Observable.FromEvent or Observable.FromEventPattern (the difference is here) to subscribe to the event something like this:
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
if (MemberAdded != null)
MemberAdded(new MemberEventArgs(member, MemberEvent.Add));
}
public event EventHandler<MemberEventArgs> MemberAdded;
}
...
Observable.FromEventPattern<MemberEventArgs>(h => memberRepository.MemberAdded += h,
h => memberRepository.MemberAdded -= h)
.Select(e => e.Member)
.Subscribe(m => Console.WriteLine("Member "+m+" added!));
In regard to your second question, you should avoid static properties - consider using something like the Event Aggregator pattern instead
Related
I'm trying to plan a publish/subscribe implementation. I've tried several times but I get stuck cause I think either I don't understand the limitations of C# generics or I haven't gotten the correct way to do it.
I would like to keep a list of Events/Delegates that are associated with a type of object. So when the Method gets called it receives an object of correct type.
Furthermore I would like the way to use it to be like this:
Hub.Subscribe<MyMessageType>( MethodThatTakes<MyMessageType>);
Hub.Publish<MyMessageType>( new MyMessageType("Message"));
Hub.Subscribe<Vector3>( MethodThatTakes<Vector3>);
Hub.Publish<Vector3>( new Vector3(45,100,0));
My problem is I don't manage to get it to work this way. It becomes more complex to use but I'm thinking that the user shouldn't need to do more since more information is not needed.
So my question is if it's possible to make it work like this with generics or maybe I misunderstand something about how a pub sub could and should work?
Example of code to register a subscription
public static void Subscribe < T > (string title = "", CallbackMethod<T>) {
Subscriptions subs;
if (subscriptions.Any(sub => sub.Type == typeof (T) && sub.Title == title)) {
subs = subscriptions.First(sub => sub.Type == typeof (T) && sub.Title == title);
} else {
subs = new Subscriptions < T > (title);
}
}
class Subscriptions < T > {
internal Type Type;
List < CallbackMethodsWithParameter < T >> subscribers;
public Subscriptions() {
Type = T.GetType();
}
}
So here I am trying to have a subscriptions class store type of return and a list with methods to callback that takes an object of the same type.
It obviously doesn't work but that's where I am now and I'm not sure if it's even possible or a good way to do it.
Your sample registration code seems overly complex for what you are trying to achieve, which is simply registering an Action<T> and then publishing a message of T to all subscriptions.
However, this is not easily mapped for your use case since Action<in T> is contravariant and you need to store your handlers as Action<object> which would be a covariant interaction. That said, you can wrap the subscription handler and cast the incoming message.
public class Hub
{
private static object _subscriptionLock = new object();
private static ConcurrentDictionary<Type, ConcurrentBag<Action<object>>> _map =
new ConcurrentDictionary<Type, ConcurrentBag<Action<object>>>();
public void Subscribe<T>(Action<T> subscriptionHandler)
{
var entryExists = _map.TryGetValue(typeof(T), out var entry);
var wrappedHandler = Wrap(subscriptionHandler);
if (!entryExists)
{
entry = new ConcurrentBag<Action<object>>();
}
entry.Add(wrappedHandler);
_map[typeof(T)] = entry;
}
public void Publish<T>(T message)
{
var entryExists = _map.TryGetValue(typeof(T), out var entry);
if (!entryExists) return;
foreach (var handler in entry)
{
handler(message);
}
}
private Action<object> Wrap<T>(Action<T> input)
{
Action<object> f = (message) => input((T)message);
return f;
}
}
This allows the type of subscription and publish API that you are looking for:
void Main()
{
var hub = new Hub();
hub.Subscribe<TestMessageA>(HandlerOne);
hub.Subscribe<TestMessageA>(HandlerTwo);
hub.Subscribe<TestMessageB>(HandlerOne);
hub.Subscribe<TestMessageB>(HandlerTwo);
hub.Publish(new TestMessageA { Title = "hey there!" });
hub.Publish(new TestMessageB { Title = "hey there again!" });
}
public void HandlerOne(TestMessageA message)
{
Console.WriteLine($"{message.Title} in HandlerOne");
}
public void HandlerTwo(TestMessageA message)
{
Console.WriteLine($"{message.Title} in HandlerTwo");
}
public void HandlerOne(TestMessageB message)
{
Console.WriteLine($"{message.Title} in HandlerOne");
}
public void HandlerTwo(TestMessageB message)
{
Console.WriteLine($"{message.Title} in HandlerTwo");
}
public class TestMessageA
{
public int Id { get; set; }
public string Title { get; set; }
}
public class TestMessageB
{
public int Id { get; set; }
public string Title { get; set; }
}
which outputs:
hey there! in HandlerOne
hey there! in HandlerTwo
hey there again! in HandlerOne
hey there again! in HandlerTwo
Currently, I am working on an API, and developers can subscribe to it to know the updates.
So right now I am implementing an interface IResult , so that I can send different parameters in the callback result. The problem right now is if in the future, I want to add a new callback, I have to add an argument in the method, and developers also need to change their method call. Is there a good solution for this?
public interface IResult
{
int i { get; set; }
}
public class ConcreteResult : IResult
{
public int i
{
get;set;
}
}
public class MyAPI
{
public delegate void MyAPIDelegate(IResult result);
public void StartService(MyAPIDelegate callback, MyAPIDelegate callback2)
{
//step 1
int i = 0;
ConcreteResult result1 = new ConcreteResult();
result1.i = i;
callback(result1);
//step 2
i += 1;
ConcreteResult result2 = new ConcreteResult();
result2.i = i;
callback2(result2);
//potentially added in the future
//i += 1;
//callback3();
}
public void main()
{
//developers use my API
StartService(developerCallback, developerCallback2);
}
private void developerCallback(IResult result)
{
Console.WriteLine(result.i);
}
private void developerCallback2(IResult result)
{
Console.WriteLine(result.i);
}
}
Oddly everyone is recommending events, but nobody is showing an example. I'll bite.
Judging by the naming conventions I'm guessing you come from Java land. (C# methods are generally PascalCase). C# has events, which make things like this much simpler. I recommend you study them up, as they're quite common in C# code.
All you have to do is define a public event on your class, and have that class invoke the event where necessary. (do ?. because an unsubscribed event is weirdly null).
Then from the consuming class, you subscribe handlers for it using +=.
This allows you to add new events in the future without your consumers having to worry about it.
public class MyAPI
{
public event Action<IResult> Callback1;
public event Action<IResult> Callback2;
public void StartService()
{
//step 1
int i = 0;
ConcreteResult result1 = new ConcreteResult();
result1.i = i;
Callback1?.Invoke(result1);
//step 2
i += 1;
ConcreteResult result2 = new ConcreteResult();
result2.i = i;
Callback2?.Invoke(result2);
//potentially added in the future
//i += 1;
//callback3();
}
}
public static class Program {
public static void Main()
{
//developers use my API
var api = new MyAPI();
api.Callback1 += DeveloperCallback;
api.Callback2 += DeveloperCallback2;
api.StartService();
}
private static void DeveloperCallback(IResult result)
{
Console.WriteLine(result.i);
}
private static void DeveloperCallback2(IResult result)
{
Console.WriteLine(result.i);
}
}
Also for simple event handlers, you can subscribe inline:
api.Callback1 += result =>
{
Console.WriteLine(result.i);
};
Or even simpler for one-liners:
api.Callback1 += result => Console.WriteLine(result.i);
Since you asked, another option a bit more heavier than simple events, but eventually more powerful is Reactive Extensions. If you want to use these, then you can write code like this:
using System.Reactive.Subjects;
public class MyAPI
{
private readonly Subject<IResult> callback1 = new Subject<IResult>();
private readonly Subject<IResult> callback2 = new Subject<IResult>();
public void StartService()
{
//step 1
int i = 0;
ConcreteResult result1 = new ConcreteResult();
result1.i = i;
callback1.OnNext(result1);
//step 2
i += 1;
ConcreteResult result2 = new ConcreteResult();
result2.i = i;
callback2.OnNext(result2);
}
public IObservable<IResult> Callback1 => this.callback1;
public IObservable<IResult> Callback2 => this.callback2;
}
public static class Program
{
public static void Main()
{
var api = new MyAPI();
// Subscribing returns a disposable subscription, and disposing it unsubscribes.
// That means you can use lambda syntax and still unsubscribe later
IDisposable subscription =
api.Callback1.Subscribe(result => Console.WriteLine(result.i));
api.StartService(); // Writes result.
// Once disposed, event is no longer called
subscription.Dispose();
api.StartService(); // Doesn't write result.
// Since IDisposable is a special thing that can be scoped to using blocks in C#, you can do the following:
using (api.Callback1.Subscribe(result => Console.WriteLine(result.i)))
{
api.StartService(); // Writes result
}
api.StartService(); // Doesn't write result
}
}
I strongly recommend using events, like #Vikhram suggested, but here is your example, modified to use a class as you requested.
Notice that I did not specify a Callback3 when calling the function. The API uses .? when calling them, instead of just ., so that it doesn't cause a NullReferenceException if the developer doesn't pass one in.
When you add more callbacks, just add additional properties to MyCallbackInfo, and invoke them the same as the existing ones.
public interface IResult {... }
public class ConcreteResult : IResult {...}
public class MyStartServiceCallbackInfo
{
public MyAPI.MyAPIDelegate Callback1 { get; set; }
public MyAPI.MyAPIDelegate Callback2 { get; set; }
public MyAPI.MyAPIDelegate Callback3 { get; set; }
}
public class MyAPI
{
public delegate void MyAPIDelegate(IResult result);
public void StartService(MyStartServiceCallbackInfo callbacks)
{
...
callbacks?.Callback1(result1);
...
callbacks?.Callback2(result2);
...
callbacks?.Callback3(result3);
}
public void main()
{
StartService(new MyCallbackInfo()
{
Callback1 = developerCallback,
Callback2 = developerCallback2,
});
}
private void developerCallback(IResult result)
{
Console.WriteLine(result.i);
}
private void developerCallback2(IResult result)
{
Console.WriteLine(result.i);
}
}
I am trying to make sort of a library and i am trying to grasp how can i implement it they way i want.
I created a minimalist example to show you what i am trying to do.
using System;
namespace example
{
public class Car
{
public int Price;
public string ModelName;
public Boolean Sold;
public delegate void SellEventHandler(string str);
public static event SellEventHandler _OnSell;
public void OnSell(string str)
{
Console.WriteLine("event was fired");
}
public Car(int price, string modelname)
{
Price = price;
ModelName = modelname;
Sold = false;
_OnSell = OnSell;
}
}
public class Program
{
static void Main()
{
Car _car = new Car(6000, "audi");
_car._OnSell += Car_OnSell;
}
public void Car_OnSell(string message)
{
Console.WriteLine(message);
}
}
}
Even though i haven't implemented when the event will be invoked ( it should be invoked when the Sold property of the _car changes ), i want to execute the OnSell(string str) method of the Car class ( prints "event was fired" ) and after that, i want to execute the Car_OnSell function ( see code _car.OnSell += Car_OnSell )
Hopefully you get the idea of what i am trying to do here. Right now the error i get is Member 'example.Car._OnSell' cannot be accessed with an instance reference; qualify it with a type name instead on the line _car.OnSell += Car_OnSell;. However i am not sure if i am going in the right direction with this at all.
I think I understand what you're doing, and here's how I would do it.
Don't hook up an event in your class. Instead, create a 'Sell' method that does whatever the class would normally do (like set Sold == true), but first check if the client hooked up your _OnSell event, and fire that first. You may want to provide some way for the client to cancel the sale in the _OnSell event as well.
You also need to make your Car_OnSell static, since you're hooking it up from a static method (Main). This is because a non-static method requires a class instance to access it.
Here's an example:
static void Main()
{
var car = new Car(6000, "audi");
car._OnSell += Car_OnSell;
car.Sell(string.Format("Selling the car: {0}", car.ModelName));
}
public static void Car_OnSell(string message)
{
Console.WriteLine(message);
}
public class Car
{
public int Price { get; set; }
public string ModelName { get; set; }
public Boolean Sold { get; set; }
public delegate void SellEventHandler(string str);
public event SellEventHandler _OnSell;
public void Sell(string str)
{
if (_OnSell != null)
{
_OnSell(str);
}
this.Sold = true;
}
public Car(int price, string modelname)
{
Price = price;
ModelName = modelname;
Sold = false;
}
}
The Reactive Extensions allow you to easily subscribe to an event using Observable.FromEventPattern, but I can't find anything on how you might implement an event when you have an IObservable.
My situation is this: I need to implement an interface which contains an event. That event is supposed to be called whenever a certain value of my object changes, and for thread safety reasons I need to call this event on a certain SynchronizationContext. I am also supposed to call each event handler with the current value on registration.
public interface IFooWatcher
{
event FooChangedHandler FooChanged;
}
Getting an observable that does what I want is rather easy with Rx using BehaviorSubject:
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged()
.ObserveOn(synchronizationContext);
}
public event FooChangedHandler FooChanged
{
add { /* ??? */ }
remove { /* ??? */ }
}
}
Now I am looking for an easy way to have the add and remove functions subscribe and unsubscribe the passed FooChangedHandler as an Observer<Foo> on m_observable. My current implementation looks similar to this:
add
{
lock (m_lock)
{
IDisposable disp = m_observable.Subscribe(value);
m_registeredObservers.Add(
new KeyValuePair<FooChangedHandler, IDisposable>(
value, disp));
}
}
remove
{
lock (m_lock)
{
KeyValuePair<FooChangedHandler, IDisposable> observerDisposable =
m_registeredObservers
.First(pair => object.Equals(pair.Key, value));
m_registeredObservers.Remove(observerDisposable);
observerDisposable.Value.Dispose();
}
}
However, I hope to find an easier solution, because I need to implement several of these events (of differing handler types). I tried to roll my own generic solution but it creates some additional problems that need to be worked around (in particular, how you generically work with a delegate that takes a parameter of T), so I would prefer to find an existing solution that bridges the gap in this direction - just as FromEventPattern does the reverse.
You could do this:
public event FooChangedHandler FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
However, on the remove, I think perhaps you just may want to dispose of the subscription ... or perhaps get the Action from ToEvent() and store that as a member. Untested.
EDIT: You'll have to use Action instead of a FooChangedHandler delegate, however.
EDIT 2: Here's a tested version. I suppose you need to use FooChangedHandler, however, since you have a bunch of these pre-existing handlers?
void Main()
{
IObservable<Foo> foos = new [] { new Foo { X = 1 }, new Foo { X = 2 } }.ToObservable();
var watcher = new FooWatcher(SynchronizationContext.Current, new Foo { X = 12 });
watcher.FooChanged += o => o.X.Dump();
foos.Subscribe(watcher.Subject.OnNext);
}
// Define other methods and classes here
//public delegate void FooChangedHandler(Foo foo);
public interface IFooWatcher
{
event Action<Foo> FooChanged;
}
public class Foo {
public int X { get; set; }
}
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
public BehaviorSubject<Foo> Subject { get { return m_subject; } }
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged();
}
public event Action<Foo> FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
}
Given that you are already mixing the boundaries between reactive and more normal code, you could do a less reactive version. To start simply declare a normal event pattern
public event FooChangedHandler FooChanged;
protected void OnFooChanged(Foo)
{
var temp = FooChanged;
if (temp != null)
{
temp(new FooChangedEventArgs(Foo));
}
}
and then simply connect the observable to it in the constructor
m_Observable.Subscribe(foo => OnFooChanged(foo));
It's not very Rx but it is incredibly simple.
What is the syntax to return an event from a function? (Not to call the event, to return it so that it can be bound to functions).
I have a container class that contains a dictionary where each members has an event.
The aim is to be able to write something like this:
Container c = new Container();
c.CreateEventForKey("a"); // Create the member in the dictionary
c.EventForKey("a") += some_function; // Bind some_function to the event in the "a" member
c.OnEventForKey("a","b"); // Calls some_function with argument "b"
The Container class looks like this:
public class Container {
public class Member {
public event Action<string> AnEvent;
public void OnEvent( string v ) { if(AnEvent!=null) { AnEvent(v); } }
}
protected Dictionary<string,Member> members;
// This seems to work OK.
public void OnEventForKey(string k, string v) {
if ( members.ContainsKey(k) ) { members[k].OnEvent(v); }
else { /* report error */ }
}
// Can't get this to compile.
public event Action<string> EventForKey(string k ) {
if ( members.ContainsKey(k) ) { return members[k].AnEvent; }
else { /* report error */ }
}
}
How can I define EventForKey so that this does what I expect?
What is the syntax to return an event from a function?
You can't, easily. Events - like properties - aren't really first class "objects" as such; they're members of a class. You don't really have a class member here - you're trying to just keep delegates in a dictionary.
You could create your own "event-like" container, but it's probably better to consider alternative designs, e.g.
c.Subscribe("a", SomeFunction);
c.OnEventForKey("a");
You might want to look at EventHandlerList for inspiration.
Why not simply return member and subscribe to it's event?
public IMember MemberForKey(string key) // return IMember
{
if (!members.ContainsKey(key))
throw new Exception();
return members[key];
}
And then subscribe:
Container c = new Container();
c.CreateEventForKey("a");
c.MemberForKey("a").AnEvent += some_function;
c.OnEventForKey("a", "b");
But you have public OnEvent method in Member class. In order to forbid raising events by client, you can create interface which will show only event. Just implement this interface by Member class:
public interface IMember
{
event Action<string> AnEvent;
}
And yes, you cannot return event, because actually event is not object, it is set of two methods add and remove, which add and remove delegates to inner field of delegate type. Here is how your event looks like:
private Action<string> _action; // field of delegate type
public event Action<string> AnEvent
{
add { _action += value; }
remove { _action -= value; }
}
Purpose of event is to provide only two operations for clients - adding and removing handlers. Delegate itself is hidden to clients. You can make it public:
public Action<string> _action;
But in this case any client can invoke it.
UPDATE: if you want to go with Subscribe/Remove syntax, then just use dictionary with handlers:
public class Container
{
private Dictionary<string, Action<string>> handlers =
new Dictionary<string, Action<string>>();
public void CreateEventForKey(string key)
{
// with empty handler added you can avoid null check
handlers.Add(key, (value) => { });
}
public void OnEventForKey(string key, string value)
{
if (!handlers.ContainsKey(key))
throw new Exception();
handlers[key](value);
}
public void Subscribe(string key, Action<string> handler)
{
if (!handlers.ContainsKey(key))
throw new Exception();
handlers[key] += handler;
}
}
Here's complete working example:
class Program
{
static void Main(string[] args)
{
Container c = new Container();
c.CreateEventForKey("a"); // Create the member in the dictionary
c.EventForKey("a").Add(str => Console.WriteLine(str));
c.EventForKey("a").Add(str => Console.WriteLine(str.ToUpper()));
c.OnEventForKey("a", "baa baa black sheep");
Console.ReadLine();
}
}
public class Container
{
public class Member
{
public List<Action<string>> AnEvent = new List<Action<string>>();
public void OnEvent(string v)
{
if (AnEvent != null)
{
this.AnEvent.ForEach(action => action(v));
}
}
public void AddEvent(Action<string> action)
{
this.AnEvent.Add(action);
}
}
protected Dictionary<string, Member> members = new Dictionary<string,Member>();
public void CreateEventForKey(string key)
{
this.members[key] = new Member();
}
// This seems to work OK.
public void OnEventForKey(string k, string v)
{
if (members.ContainsKey(k)) { members[k].OnEvent(v); }
else { /* report error */ }
}
public List<Action<string>> EventForKey(string k)
{
if (members.ContainsKey(k)) { return members[k].AnEvent; }
else { throw new KeyNotFoundException(); }
}
}
The difference is to behave similarly to an event by using a list of delegates.