[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 |
Beta Was this translation helpful? Give feedback.
-
Well, properties are another matter... |
Beta Was this translation helpful? Give feedback.
-
That's a good point. It also partially applies to events: Though that delegate can never be of type |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
I previously started a discussion on codeplex here for this exact feature but never received any feedback. |
Beta Was this translation helpful? Give feedback.
-
FSharp exposes event as IObservable. Events as done by C# are just wrong. See the fsharp intro to the first class events. |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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.. |
Beta Was this translation helpful? Give feedback.
-
+1 for making C# events first class citizen |
Beta Was this translation helpful? Give feedback.
-
@Liero I don't get it. Events are already first class citizen in C# |
Beta Was this translation helpful? Give feedback.
-
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...) |
Beta Was this translation helpful? Give feedback.
-
@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. |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
Replace Task with IObservable and all is well. On Sat, 13 Aug 2016, 14:36 cptjazz, notifications@github.com wrote:
|
Beta Was this translation helpful? Give feedback.
-
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:
|
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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>
.
|
Beta Was this translation helpful? Give feedback.
-
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 ) { }
} |
Beta Was this translation helpful? Give feedback.
-
Related to @svick's suggestion for first class properties: #1901 |
Beta Was this translation helpful? Give feedback.
-
Thoughts RE this and #1901 - |
Beta Was this translation helpful? Give feedback.
-
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); |
Beta Was this translation helpful? Give feedback.
-
Maybe event is actually should be considered the obsolete legacy and we should migrate to And we might have build time codegen to emit |
Beta Was this translation helpful? Give feedback.
-
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);
}
}
} |
Beta Was this translation helpful? Give feedback.
-
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
EventDescriptor
object (or some other appropriate name).Now the
WhenEventRaised
method could have the following signature:And the compiler would implicitly transform this:
into this:
Beta Was this translation helpful? Give feedback.
All reactions