event
s) either does not exist at all, or exists with terrible force, but in a very predictable way. For example, when someone clicks a button and a button_Click
event is button_Click
.public class FootballPlayer<br/>
{<br/>
public string Name { get;set; }<br/>
public void Score()<br/>
{<br/>
var e = Scored;<br/>
if (e != null ) e( this , new EventArgs());<br/>
}<br/>
public event EventHandler Scored; <br/>
}<br/>
public class FootballCoach<br/>
{<br/>
public void Watch(FootballPlayer p)<br/>
{<br/>
p.Scored += (_, __) =><br/>
{<br/>
Console.WriteLine( "I'm happy that {0} scored" , p.Name);<br/>
};<br/>
}<br/>
}<br/>
var p = new FootballPlayer { Name = "Arshavin" };<br/>
var c = new FootballCoach();<br/>
c.Watch(p);<br/>
p.Score(); // coach saw it!
MemberwiseClone()
or deep copy using BinaryFormatter
), all subscriptions of this object will be lost.var p2 = c.Clone(); // deep copy :)
p.Score();<br/>
Expression<T>
, something like that. But this is only part of the problem.class FootballCoach<br/>
{<br/>
public FootballCoach(FootballPlayer[] players)<br/>
{<br/>
foreach ( var p in players) {<br/>
p.EntersField += new EventHandler(StartWatchingPlayer);<br/>
p.LeavesField += new EventHandler(StopWatchingPlayer);<br/>
}<br/>
}<br/>
}<br/>
StartXxx
you will subscribe, in each EndXxx
unsubscribe. But that's not all.+=
, we will get brutal connectedness and complete non-testability (and testing messages is generally difficult) of our code.public class EventBroker<br/>
{<br/>
private MultiDictionary< string , Delegate> subscriptions = <br/>
new MultiDictionary< string , Delegate>( true );<br/>
public void Publish<T>( string name, object sender, T args)<br/>
{<br/>
foreach ( var h in subscriptions[name])<br/>
h.DynamicInvoke(sender, args);<br/>
}<br/>
public void Subscribe<T>( string name, Delegate handler)<br/>
{<br/>
subscriptions.Add(name, handler);<br/>
}<br/>
}<br/>
ReaderWriterLockSlim
), etc. but the essence will not change. We received a broker who can replace event subscriptions. Of course, you won’t get any QoS here, and you’ll have to write all the logic related to event sampling with pens, but there are already some moves - for example, by including the name
as a classifier, we created a situation in which one class can sign a handler for several events at the same time.public class FootballPlayer<br/>
{<br/>
private readonly EventBroker broker;<br/>
public string Name { get; set; }<br/>
public FootballPlayer(EventBroker broker)<br/>
{<br/>
this .broker = broker;<br/>
}<br/>
public void Injured()<br/>
{<br/>
broker.Publish( "LeavingField" , this , new PlayerInjuredEventArgs());<br/>
}<br/>
public void SentOff()<br/>
{<br/>
broker.Publish( "LeavingField" , this , new PlayerSentOffEventArgs());<br/>
}<br/>
}<br/>
public class FootballCoach<br/>
{<br/>
private readonly EventBroker broker;<br/>
public FootballCoach(EventBroker broker)<br/>
{<br/>
this .broker = broker;<br/>
}<br/>
public void Watch(FootballPlayer player)<br/>
{<br/>
broker.Subscribe<EventArgs>( "LeavingField" ,<br/>
new EventHandler(PlayerIsLeavingField));<br/>
}<br/>
public void PlayerIsLeavingField( object sender, EventArgs args)<br/>
{<br/>
Console.WriteLine( "Where are you going, {0}?" ,<br/>
(sender as FootballPlayer).Name);<br/>
}<br/>
}<br/>
EventArgs
. Strong typing is not critical here. you can always do a cast. Here's how it will look like:var uc = new UnityContainer();<br/>
uc.RegisterType<EventBroker>(<br/>
new ContainerControlledLifetimeManager());<br/>
var p = uc.Resolve<FootballPlayer>();<br/>
p.Name = "Arshavin" ;<br/>
var c = uc.Resolve<FootballCoach>();<br/>
<br/>
c.Watch(p);<br/>
<br/>
p.Injured();<br/>
p.SentOff();<br/>
public class FootballCoach<br/>
{<br/>
[SubscribesTo( "PlayerIsLeaving" )]<br/>
public void PlayerIsLeavingField( object sender, EventArgs args)<br/>
{<br/>
Console.WriteLine( "Where are you going, {0}?" ,<br/>
(sender as FootballPlayer).Name);<br/>
}<br/>
}<br/>
Source: https://habr.com/ru/post/97285/
All Articles