[Proposal] Make events first class citizens in C# #2506
Replies: 25 comments 2 replies
|
What about properties? Sure, needing both getter and setter for properties is less common than needing both the subscriber and unsubscriber for events, but it could still be useful. It would also mean, that to pass a setter, you could write just |
|
Well, properties are another matter... |
|
That's a good point. It also partially applies to events: Though that delegate can never be of type |
|
Something like this would be nice and probably should be considered alongside #261 as events are likely to be a common source for asynchronous streams. Rx currently uses code similar to your example to manually wire up the event accessors and it does feel a little kludgy. You'd probably want two flavors of this kind of construct. One that works with raw delegates of any type that do not provide a predictable parameter list, and another that works with |
|
I just realized something... In my example, I said in a comment that "ideally, having a delegate constraint here would be better". But it wouldn't just be better, it would be necessary for this approach to work, because |
|
I previously started a discussion on codeplex here for this exact feature but never received any feedback. |
|
FSharp exposes event as IObservable. Events as done by C# are just wrong. See the fsharp intro to the first class events. |
Not a terribly constructive statement and I think a bit unfair. F# had the benefit of coming quite a bit later and could benefit from the reactive philosophies building around observables. It might be more useful to look into how C# events could be better bridged with observables. I'm thinking specifically syntax that would allow a consumer of any existing C# event to automatically obtain a wired |
|
I apologize to C#. I'm sure it will forgive me. I use it every day and generally we get along fine :). Then there are days like when I wrote the following T4 script https://github.com/Weingartner/SolidworksAddinFramework/blob/master/SolidworksAddinFramework/ModelDocEvents.tt. It parses the large Solidworks API event library and exposes most events as IObservable. I also wrote the first version of a similar tool in ReactiveUI except now it's replaced with a Mono Cecil code generator. It's certainly an ugly hack and I would appreciate for the runtime to support this mapping automagically.. |
|
+1 for making C# events first class citizen |
|
@Liero I don't get it. Events are already first class citizen in C# |
Well, the expression "first class citizen" is a bit vague... sure, you can already use events in C#; but you can't pass them around, and the need to do that is getting more and more common (Rx, tasks...) |
|
@stepanbenes "First class" is defined well(ish) in computer science. Try http://c2.com/cgi/wiki/?FirstClass
By this definition events in C# are not first class. They are an extension on the language and can only be used by special case notation. IObservable on the otherhand is first class and instances can be passed around like any other value. |
|
I think converting an event to a Task (as in the original description of this issue) does not make sense at all. The consumer of the Task would only be able to observe the first invocation of the event. The Task is then in a completed state and cannot be used to observe further event invocations. Furthermore when I pass the Task around I get some kind of "replaying", because a second consumer that awaits the Task at a later point in time would be tricked into thinking that the event was raised "now" when in fact the Task could have been completed 10 minutes ago. I think if you want events to be first class a distinct |
|
Replace Task with IObservable and all is well. On Sat, 13 Aug 2016, 14:36 cptjazz, notifications@github.com wrote:
|
|
True :) |
|
if events were first class citizen in C#, it would be possible to write extension methods, which would allow to easily convert them to IObservable: on the other side, this would be interesting as well: |
|
I need this feature so desperately! 🤤 event EventHandler<int> AllFinished;
async Task<int> DoThings(string name)
{
InitiateAnAction(name);
var n = await AllFinished.Once( (s,e) => {e.name == name} );
return n;
}From my piece of code, it may seem as a bad design but believe me, in this scenario there is no better way. And I genuinely believe there are more scenarios where converting events to tasks (or generally working with events as first-class citizens) would be very useful. If events could be passed as parameters, this all would be a matter of writing a simple extension method: public static Task<(object sender, T args)>
Once<T>(this EventHandler<T> e, Func<object, T, bool> when)
{
// ...
}However now |
|
This is not bad design. This is "the right way" :)
…On Tue, 27 Jun 2017, 10:20 Michal Grňo ***@***.***> wrote:
I need this feature so desperately! 🤤
In my methods I very often need to wait until an event fires before
returning. If I could write this, it would save me lots of work:
event EventHandler<int> AllFinished;
async Task<int> DoThings(string name)
{
InitiateAnAction(name);
var n = await AllFinished.Once( (s,e) => {e.name == name} );
return n;
}
From my piece of code, it may seem as a bad design but believe me, in
this scenario
<https://stackoverflow.com/questions/44463255/make-c-sharp-method-wait-for-js-event-to-happen>
there is no better way. And I genuinely believe there are more scenarios
where converting events to tasks (or generally working with events as
first-class citizens) would be very useful.
If events could be passed as parameters, this all would be a matter of
writing a simple extension method:
public static Task<(object sender, T args)>
Once<T>(this EventHandler<T> e, Func<object, T, bool> when)
{
// ...
}
However now e += d and e -= d do nothing inside this method.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://github.com/dotnet/roslyn/issues/298#issuecomment-311287663>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AABE8teT0jWfLaF5bLQA_ta55b-8psMsks5sILsygaJpZM4DdN88>
.
|
|
As @YaakovDavis pointed out there are some differences between events and observables. My 2¢ is that maybe it would feel more natural if we just added more places where the class Foo
{
EventHandler Bar; //this is a handler
event EventHandler Qux; //this is an event
}What if we allowed it on places like this? class Foo
{
void Bar(event EventHandler e) { }
event EventHandler Qux() { }
public static Task Once( this event EventHandler e ) { }
//not sure about these two
void Plugh( out event EventHandler e ) { }
void Corge( ref event EventHandler e ) { }
} |
|
Thoughts RE this and #1901 - |
|
I also feel this is an important area that could reduce a lot of tedious and redundant verbosity, plus remove many limitations and allow extension methods to run on events.
But alternatively in the meantime, at least if you'd removed the limitation of addressing an event from outside its owner class without public static void ToObservable<TSender, TDelegate>(
this TSender sender,
Expression<Func<TSender, TDelegate>> eventSelector)
where TDelegate : Delegate
{
/* getEventInfo from Linq expression and attach/detach System.Rective handlers */
}
//usage
IObservable<EventPattern<PropertyChangedEventArgs>> observable =
myObj.ToObservable(obj => obj.PropertyChanged); |
|
Maybe event is actually should be considered the obsolete legacy and we should migrate to And we might have build time codegen to emit |
|
Could this be done as compiler feature without runtime changes? I mean existing IObservable already covers vast majority of use cases so we really only need neat way to convert event to IObservable. e.g class SomeClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
var someClassInstance = new SomeClass();
IObservable<PropertyChangedEventArgs> observable = someClassInstance.PropertyChanged.ToObservable();would transpile to:
IObservable<PropertyChangedEventArgs> observable = new SomeClassPropertyChangedObservable (someClassInstance);
[CompilerGenerater]
public class SomeClassPropertyChangedObservable : IObservable<PropertyChangedEventArgs>
{
readonly SomeClass _source;
public SomeClassPropertyChangedObservable (SomeClass source)
{
_source = source;
}
public IDisposable Subscribe(IObserver<PropertyChangedEventArgs> observer)
{
return new EventSubscriber(observer, _source);
}
private class EventSubscriber : IDisposable
{
readonly IObserver<PropertyChangedEventArgs> _observer;
readonly SomeClass _source;
public EventSubscriber(IObserver<PropertyChangedEventArgs> observer, SomeClass source)
{
_observer = observer;
_source = source;
_source.PropertyChanged += EventHandler;
}
public void Dispose()
{
_source.PropertyChanged -= EventHandler;
}
void EventHandler(object source, PropertyChangedEventArgs args)
{
_observer.OnNext(args);
}
}
} |
Uh oh!
There was an error while loading. Please reload this page.
Currently in C# there is no clean way to pass an event around. There are workarounds, such as passing delegates for subscription/unsubscription, or using reflection, but they're not convenient at all. I want to be able to write something like this:
The only two operations currently allowed on events are subscription and unsubscription. We could add a third: conversion to an
EventDescriptorobject (or some other appropriate name).Now the
WhenEventRaisedmethod could have the following signature:And the compiler would implicitly transform this:
into this:
All reactions