How to subscribe an EventHandler? [Answered]

I don't understand how to subscribe this. I don't get it. I honestly don't even know why I used an EventHandler instead of an Action or something.
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs>? Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public Notifier(CharberryTree tree)
{
tree.Ripened += OnRipened();
}
public void OnRipened()
{

}
}

public class Harvester
{
public Harvester(CharberryTree tree)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs>? Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public Notifier(CharberryTree tree)
{
tree.Ripened += OnRipened();
}
public void OnRipened()
{

}
}

public class Harvester
{
public Harvester(CharberryTree tree)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
230 Replies
BigggMoustache
actually at this point I'm sure I'm doing the exercise wrong.
sibber
sibber2y ago
tree.Ripened += OnRipened; you dont want to invoke the method, you want the method itself
BigggMoustache
yeah that's right. I just changed the EventHandler to an Action? also
sibber
sibber2y ago
also Notifier should be called NotificationHandler, it doesnt notify anything sure
BigggMoustache
Oh really okay
sibber
sibber2y ago
or preferably change the handler signature
BigggMoustache
Idk what that means lol
sibber
sibber2y ago
public void OnRipened(object? sender, RipenedEventArgs args)
{

}
public void OnRipened(object? sender, RipenedEventArgs args)
{

}
BigggMoustache
Okay yeah I could have done that.
sibber
sibber2y ago
the handler's signature has to match the event delegate's signature
BigggMoustache
yeah thanks. Give me one second to figure out next question.
sibber
sibber2y ago
which is EventHandler<RipenedEventArgs>
BigggMoustache
Okay I remember reading that EventArgs is how data is passed
sibber
sibber2y ago
yeah thats one way to pass data its the convention tho
BigggMoustache
and my interpretation was using that to pass the Ripe value of the tree did I do that correctly?
sibber
sibber2y ago
sure yeah
BigggMoustache
okay I'm changing back to EventHandler then lol sec
sibber
sibber2y ago
maybe name it IsRipe to be clearer
BigggMoustache
in the eventarg?
sibber
sibber2y ago
and the class
BigggMoustache
cause the top portion is part copied from example ye i dun wanna do that lol
sibber
sibber2y ago
ah fair
BigggMoustache
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();
public class CharberryTree
{
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
}
}
}
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();
public class CharberryTree
{
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
}
}
}
This is the reference it gave
Objectives:
• Make a new project that includes the above code.
• Add a Ripened event to the CharberryTree class that is raised when the tree ripens.
• Make a Notifier class that knows about the tree (Hint: perhaps pass it in as a constructor parameter) and subscribes to its Ripened event. Attach a handler that displays something like “A charberry fruit has ripened!” to the console window.
• Make a Harvester class that knows about the tree (Hint: like the notifier, this could be passed as a constructor parameter) and subscribes to its Ripened event. Attach a handler that sets the tree’s Ripe property back to false.
• Update your main method to create a tree, notifier, and harvester, and get them to work together to grow, notify, and harvest forever
Objectives:
• Make a new project that includes the above code.
• Add a Ripened event to the CharberryTree class that is raised when the tree ripens.
• Make a Notifier class that knows about the tree (Hint: perhaps pass it in as a constructor parameter) and subscribes to its Ripened event. Attach a handler that displays something like “A charberry fruit has ripened!” to the console window.
• Make a Harvester class that knows about the tree (Hint: like the notifier, this could be passed as a constructor parameter) and subscribes to its Ripened event. Attach a handler that sets the tree’s Ripe property back to false.
• Update your main method to create a tree, notifier, and harvester, and get them to work together to grow, notify, and harvest forever
This is the instruction and my first post is what I've done
sibber
sibber2y ago
i think the harvester is the one that should subscribe to the event
BigggMoustache
So my problem with the EventHandler thing is I don't sub the event through the ctor like instructed
sibber
sibber2y ago
wdym
BigggMoustache
the instruction suggest taking the tree through the other classes constructor to sub the event in the CharberryTree class
sibber
sibber2y ago
yeah whats the problem with that
BigggMoustache
I'd have to set a property / field to hold the what the constructor paramter takes in, and then reference that in the class taking the EventArg? I'm just not sure how it works lol. The EventArg example didn't have any stuff poppin' in a constructors parameters.
sibber
sibber2y ago
well you only need to subscribe to the event which you already did
public class Notifier
{
public Notifier(CharberryTree tree)
{
tree.Ripened += OnRipened();
}
public void OnRipened()
{

}
}
public class Notifier
{
public Notifier(CharberryTree tree)
{
tree.Ripened += OnRipened();
}
public void OnRipened()
{

}
}
so all thats left for this class is to print a message to the console saying the tree has ripened anyway gtg its 2 am goodluck
BigggMoustache
ty lol gn homie well I gott ajet anyways. I guess I'll finish this later too. xD. Thanks again homie.
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
My event in Notifier is not being called. it's getting to my Ripened?.Invoke() line and going back to the while loop at start I'm noticing my EventHandler<RipenedEventArgs> ?Ripened; is null when invoked and I'm not sure why that is either.
ero
ero2y ago
You never subscribe anything to tree.Ripened
BigggMoustache
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
}
public void OnRipened(object sender, RipenedEventArgs args)
{

}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
If I understand correctly tree.Ripened += OnRipened; in the public Notifier(CharberryTree tree) subscribes the event? Sorry I forgot to update code
ero
ero2y ago
and what part of your code calls the Notifier constructor?
BigggMoustache
I thought the MaybeGrow() method did with Ripened.Invoke()
ero
ero2y ago
no, none of that code contains a call to any constructor
BigggMoustache
Oh I never made a Notifier or Harvester so I can't use them lol tyvm. Hey so quick question about what was happenig
BigggMoustache
before I created the new objects this was always null
BigggMoustache
I don't understand.
ero
ero2y ago
.
BigggMoustache
so the event being null is having nothing subscribed okay that's the first time I've ran into anything that was null because of a relationship to something else iirc so I have another question I guess well one moment let me collect mythoughts so the goal of the exercise is to have the tree ripen, notify, and havest (Ripe = false again) how to order events?
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
Foo foo = new();

foo.BarNull();
foo.Bar += (s, e) => { };
foo.BarNull();

class Foo
{
public event EventHandler<string>? Bar;
public void BarNull() => Console.WriteLine(Bar is null);
}
Foo foo = new();

foo.BarNull();
foo.Bar += (s, e) => { };
foo.BarNull();

class Foo
{
public event EventHandler<string>? Bar;
public void BarNull() => Console.WriteLine(Bar is null);
}
Console Output
True
False
True
False
Compile: 780.351ms | Execution: 89.895ms | React with ❌ to remove this embed.
ero
ero2y ago
i'm not super familiar with event patterns unfortunately
BigggMoustache
I'll be hoenst that shoots entirely over my head no problem. could you explain the thing?
ero
ero2y ago
what thing? i assume foo.Bar += (s, e) => { }; is what confuses you?
BigggMoustache
yes
ero
ero2y ago
just a very shorthand form of subscribing
BigggMoustache
I don't get the output lol so maybe the whole thing confuses xD
ero
ero2y ago
oh i was just showing whether foo.Bar was null or not showing that it is null before subscribing anything, and not null after
BigggMoustache
oh okay OH you were showing the error I had okay yeah tyvm. That was a new experience, where the relation itself is the content checked yeah that is illuminating. could you explain the shorthand? what is s, e?
ero
ero2y ago
like i'd personally just have Actions for this, i rarely use delegates or events sender and whatever type the EventHandler takes (in my case, string)
BigggMoustache
I used EventHandler because it seemed like the easiest way to share the Ripe property in the event
ero
ero2y ago
i just woulnd't even share it like why would another class care about my tree growing and ripening
BigggMoustache
well my goal was just to play with it because I'd never used it. well Harvester *will set *Ripe back to false so I was going to try to use the event data to determine that So just wanted to explore the functions I guess Why do you like Actions? Oh yeah I forgot EventHandler has two parameters
ero
ero2y ago
!e
Tree tree = new Tree();
tree.OnRipen = _ => Console.WriteLine("My tree ripened!");

while (true)
tree.TryGrow();


class Tree
{
public bool IsRipe { get; set; }
public Action<Tree>? OnRipen { get; set; }

public void TryGrow()
{
if (Random.Shared.Next() < 0.00000001 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this);
}
}
}
Tree tree = new Tree();
tree.OnRipen = _ => Console.WriteLine("My tree ripened!");

while (true)
tree.TryGrow();


class Tree
{
public bool IsRipe { get; set; }
public Action<Tree>? OnRipen { get; set; }

public void TryGrow()
{
if (Random.Shared.Next() < 0.00000001 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this);
}
}
}
MODiX
MODiX2y ago
Ero#1111
REPL Error
An error occurred while sending a request to the REPL service. This may be due to a StackOverflowException or exceeding the 30 second timeout. Details: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
Tried to execute
ero
ero2y ago
whoops, probability too small
BigggMoustache
It does seem easier overall to use lol
ero
ero2y ago
but maybe there are some patterns i don't know about
BigggMoustache
shrug idk either lol. I just know from example reference EventHandler was made to pass data around for you so I wanted to give it a go. I'll ask for criticism once complete in a minute or two.
BigggMoustache
CharberryTree tree = new CharberryTree();
Notifier notifier = new Notifier(tree);
Harvester harvester = new Harvester(tree);

while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
Tree.Ripe = false;
}
}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
CharberryTree tree = new CharberryTree();
Notifier notifier = new Notifier(tree);
Harvester harvester = new Harvester(tree);

while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
Tree.Ripe = false;
}
}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
Pobiega
Pobiega2y ago
Re event vs action, there are a few tradeoffs events can have multiple listeners, for one
BigggMoustache
So I'll be honest I'm confused why in the public class Harvester method OnRipened using Tree.Ripe = false; sets the tree object bool of Ripe back to false sorry for interrupting Pobiega
Pobiega
Pobiega2y ago
np and events sort of represent a less tightly coupled integration, imho
BigggMoustache
okay 1 listener on Action sounds weird, why is that?
Pobiega
Pobiega2y ago
wdym? thats just how it is
BigggMoustache
lol okay idk
Pobiega
Pobiega2y ago
`cs
Tree tree = new Tree();
tree.OnRipen = _ => Console.WriteLine("My tree ripened!");

while (true)
tree.TryGrow();


class Tree
{
public bool IsRipe { get; set; }
public Action<Tree>? OnRipen { get; set; }

public void TryGrow()
{
if (Random.Shared.Next() < 0.00000001 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this);
}
}
}
Tree tree = new Tree();
tree.OnRipen = _ => Console.WriteLine("My tree ripened!");

while (true)
tree.TryGrow();


class Tree
{
public bool IsRipe { get; set; }
public Action<Tree>? OnRipen { get; set; }

public void TryGrow()
{
if (Random.Shared.Next() < 0.00000001 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this);
}
}
}
this code that Ero posted for example once you set the OnRipen action, how would someone else add their listener to it? they can't. they'd replace the current one events support the += MyHandler tjhing out of the box
BigggMoustache
I didn't try the multidelegate(probably wrong word) or chaining thing that was talked of in book is that event specific?
Pobiega
Pobiega2y ago
Action is a delegate type
BigggMoustache
OH okay
Pobiega
Pobiega2y ago
nah you're correct events are supported by a multi delegate
BigggMoustache
okay thanks for that. why event vs eventhandler then?
Pobiega
Pobiega2y ago
hm? well an event is an event honestly that part is a bit confusing
Pobiega
Pobiega2y ago
Handling and Raising Events
Learn to handle and raise .NET events, which are based on the delegate model. This model lets subscribers register with or receive notifications from providers.
Pobiega
Pobiega2y ago
class Counter
{
public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}

// provide remaining implementation for the class
}
class Counter
{
public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}

// provide remaining implementation for the class
}
EventHandler is a type, much like Counter or string etc so to make an event, you say event MyEventType MyEventName;
BigggMoustache
right right
Pobiega
Pobiega2y ago
the type declares what parameters a handler must have
BigggMoustache
Oh so it's a premade type of event
Pobiega
Pobiega2y ago
but since its all delegates in the background, you can assign any method to be your "handler"
BigggMoustache
yeah okay
Pobiega
Pobiega2y ago
EventHandler is a premade delegate type, yes
BigggMoustache
dude this is wild lol
Pobiega
Pobiega2y ago
mhm it can be confusing but ITS ALL DELEGATES 😄
BigggMoustache
I know lmaooooo the book made sure to emphasize over and over that it was uh, delegates underneath
Pobiega
Pobiega2y ago
essentially, use event for actual events, like OnRipened etc
BigggMoustache
made me think of inumerable under things so like Linq
Pobiega
Pobiega2y ago
I tend to use Action like "events" when I do testing stubs since I know I will never ever have more than one "listener/handler"
BigggMoustache
okay yeah. I'm finding things up until this point were super easy but things like this are like, multi step processes you put on top of all the other stuff idk haha.
Pobiega
Pobiega2y ago
I seem to recall you asking some questions before, so not "super easy" :p
BigggMoustache
I tried learning events a long time ago when I had a worse grasp of C# LOL yeah I'm just saying it's like a new layer of abstract concept to slap on the top of things
Pobiega
Pobiega2y ago
haha yeah it pretty much is
BigggMoustache
could you offer whatever basic criticism of what I wrote?
CharberryTree tree = new CharberryTree();
Notifier notifier = new Notifier(tree);
Harvester harvester = new Harvester(tree);

while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
Tree.Ripe = false;
}
}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
CharberryTree tree = new CharberryTree();
Notifier notifier = new Notifier(tree);
Harvester harvester = new Harvester(tree);

while (true)
tree.MaybeGrow();

public class CharberryTree
{
public event EventHandler<RipenedEventArgs> ?Ripened;

public CharberryTree()
{

}
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
Ripened?.Invoke(this, new RipenedEventArgs(Ripe));
}
}
}

public class Notifier
{
public CharberryTree Tree { get; set; }
public Notifier(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree) Console.WriteLine("Tree has ripened!");
}
}

public class Harvester
{
public CharberryTree Tree { get; set; }
public Harvester(CharberryTree tree)
{
Tree = tree;
tree.Ripened += OnRipened;
}
public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
Tree.Ripe = false;
}
}
}

public class RipenedEventArgs : EventArgs
{
public bool Ripe { get; set; }
public RipenedEventArgs(bool ripe) => Ripe = ripe;
}
OOHHH I asked earlier about that Tree.Ripe = false toward the bottom
Pobiega
Pobiega2y ago
public event EventHandler<RipenedEventArgs> ?Ripened; that questionmark really bothers me its part of the type, not the name
BigggMoustache
Oh okay I always forget where those go tbh lol I thought it was for the variable specifically xD
Pobiega
Pobiega2y ago
well it is, but its part of the type you are saying this variable is of type EventHandler<RipenedEventArgs>? so it can be null so, this seems okay. I'd perhaps change the Tree.Ripe = false to be tree.Harvest() or something depends on how you wanna divvie up your responsibliites
BigggMoustache
yeah ofc that makes sense
Pobiega
Pobiega2y ago
should a tree know how to be harvested, or should a harvester know about trees?
BigggMoustache
OH BUT HEY that tree thing there how does referencing the class property change the other classes property
Pobiega
Pobiega2y ago
hm? wdym
ero
ero2y ago
Exactly that way
Pobiega
Pobiega2y ago
yeah exactly that way :p
BigggMoustache
Tree in that case is of the Harvester class
ero
ero2y ago
It's a reference
BigggMoustache
OH
Pobiega
Pobiega2y ago
its not a copy its a reference to the actual thing $refvsvalue
BigggMoustache
shoot I did not realize that
ero
ero2y ago
Classes are always passed byref
BigggMoustache
I think I need a cheat sheet to put that on lol
Pobiega
Pobiega2y ago
your "architecture" here is a bit unusual too in that your notifier/harvester both hold a hard ref to the tree instead of just registering their listeners and getting the tree that ripened from there what if you had one harvester responsible for 10 trees? the event args should contain the tree that ripened (its the sender in your case)
BigggMoustache
yeah yeah okay so just the constructor param is enough? well the Harvester uses the reference
ero
ero2y ago
Perhaps you'd want a static harvester class which acts as an extension on the tree
BigggMoustache
I don't understand that tbh but I'm excited to be exposed to the idea lol
Pobiega
Pobiega2y ago
public class Harvester
{
public Harvester(CharberryTree tree)
{
tree.Ripened += OnRipened;
}

public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree tree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
tree.Ripe = false;
}
}
}
public class Harvester
{
public Harvester(CharberryTree tree)
{
tree.Ripened += OnRipened;
}

public void OnRipened(object sender, RipenedEventArgs args)
{
if (sender is CharberryTree tree && args.Ripe == true)
{
Console.WriteLine("Havesting Charberry.");
tree.Ripe = false;
}
}
}
BigggMoustache
Also I tried using the sender object to get the tree and couldn't figure it out OH okay sender is not a reference to it
Pobiega
Pobiega2y ago
obviously, since you register your handler in the ctor, this isn't super useful you'd probably move that out to a new method no it is, but its being sent as object so you do a typecheck
ero
ero2y ago
I said static extension for a reason
Pobiega
Pobiega2y ago
if sender is CharberryTree tree yup
BigggMoustache
hmm that's cool okay can you give example of static here??
Pobiega
Pobiega2y ago
your harvester doesnt really need to be what it is currently
BigggMoustache
oh?
Pobiega
Pobiega2y ago
yeah all it does is change something on the source and print a message to the console
ero
ero2y ago
Ok i can't code this on my phone gimme a bit
Pobiega
Pobiega2y ago
that could easily be a static method an extension method to register it with would be neat
BigggMoustache
Oh well I'm just following exercise instruction which was to make it a class
Pobiega
Pobiega2y ago
yeah, thats fine
BigggMoustache
but I would love to know what you're talking about yeah dude I'm here to learn lmao, this is fucking great
Pobiega
Pobiega2y ago
because most of the time, you'll want it to be a class
BigggMoustache
nice.
Pobiega
Pobiega2y ago
its just that because this example is so simple, and doesnt need any instance data... I'm sure Ero will post the code once they write it up
BigggMoustache
Oh that's right lol. @Ero Take your time bud no rush. I really, really appreciate the instruction. so sender just grabs the object for a typecheck, it does not serve as a reference to the object as the type it is?
Pobiega
Pobiega2y ago
Also, regardless of how helpful you are, you really need to watch your snark. You are way to snarky way to often.
BigggMoustache
so just to clarify, I can send bunches of EventArgs in params and set them, and they all get passed around with reference to the object they come from?
Pobiega
Pobiega2y ago
? look at how you invoke the event Ripened?.Invoke(this, new RipenedEventArgs(Ripe)); the first parameter this is the sender that gets exposed to any listener as does the event args, but its the same eventargs being passed to all listeners, one by one
BigggMoustache
right. so I could put new RipenedEventArgs(Ripe, Age, Season, ...) in there?
Pobiega
Pobiega2y ago
yeah your eventargs is just an object with a bunch of props on it "here is the info related to the event"
BigggMoustache
so its use is to pass all that around without needing a direct reference to the object? for decoupling?
Pobiega
Pobiega2y ago
for example, on an "OnClick" event it contains the mouse location, what button was pressed, etc
BigggMoustache
ye
Pobiega
Pobiega2y ago
well you need the object ref to register the listener/handler but that might have happened via some abstraction yeah its a way to invert the dependency actually instead of your tree knowing about the harvester, its your harvester that knows about the tree at least initially
BigggMoustache
That's so cool. 'hey look over here. It's a sender with args lol
Pobiega
Pobiega2y ago
do you know what the garbage collector is btw?
BigggMoustache
oh yes I meant to ask how that'd work here unsubscribing in practice
Pobiega
Pobiega2y ago
yeah. unsubbing is the hard part events often lead to memory leaks, becuse if you dont unsub, the registered handler will keep the event source alive forever only when all the handlers get collected would the ref count reach 0 and the event source be collected
BigggMoustache
yeah all of that is wooshing I mean I vaguely get it lol
Pobiega
Pobiega2y ago
the super simplified version of the garbage collector is that it just looks at how many references there are to an object
BigggMoustache
can you show me in my code where it'd be practical given some additional circumstance if necessary?
Pobiega
Pobiega2y ago
if that number reaches 0, the object gets cleaned up ? what exactly?
BigggMoustache
in my exercise, where would I unsub the events, and if not anywhere, what premise would provide a practical place?
Pobiega
Pobiega2y ago
uhm, well your tree grows forever in a while(true)
BigggMoustache
yeah I know lol
Pobiega
Pobiega2y ago
so you dont have any leaks.
BigggMoustache
I'm just saying to further my practical understanding of the matter so it's less of a floaty abstraction
Pobiega
Pobiega2y ago
imagine if you had multiple trees, and each tree had a chance to die each time it was harvested
BigggMoustache
ya
Pobiega
Pobiega2y ago
upon death, you'd want to unsub any and all listeners the common way to do this is to just assign null to the event
ero
ero2y ago
Tree tree =
new Tree()
.AddNotifier<MyRipenNotifier>()
.AddHarvester<MyTreeHarvester>();

while (true)
tree.TryGrow();

class Tree
{
public bool IsRipe { get; set; }
public EventHandler<RipenEventArgs>? OnRipen;

public void TryGrow()
{
if (Random.Shared.Next() < 0.1 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this, new(IsRipe));
}
}
}

static class TreeFarm
{
public static Tree AddNotifier<T>(this Tree tree) where T : IRipenNotifier
{
tree.OnRipen += T.OnNotify;
return tree;
}

public static Tree AddHarvester<T>(this Tree tree) where T : IHarvester
{
tree.OnRipen += T.OnHarvest;
return tree;
}
}

interface IRipenNotifier
{
static abstract void OnNotify(object? sender, RipenEventArgs e);
}

class MyRipenNotifier : IRipenNotifier
{
public static void OnNotify(object? sender, RipenEventArgs e)
{
if (sender is not Tree tree)
return;

Console.WriteLine("Tree is now ripe!");
}
}

interface IHarvester
{
static abstract void OnHarvest(object? sender, RipenEventArgs e);
}

class MyTreeHarvester : IHarvester
{
public static void OnHarvest(object? sender, RipenEventArgs e)
{
if (sender is not Tree tree)
return;

Console.WriteLine("Harvesting that tree!");
tree.IsRipe = false;
}
}

class RipenEventArgs : EventArgs
{
public RipenEventArgs(bool isRipe)
{
Ripened = isRipe;
}

public bool Ripened { get; }
}
Tree tree =
new Tree()
.AddNotifier<MyRipenNotifier>()
.AddHarvester<MyTreeHarvester>();

while (true)
tree.TryGrow();

class Tree
{
public bool IsRipe { get; set; }
public EventHandler<RipenEventArgs>? OnRipen;

public void TryGrow()
{
if (Random.Shared.Next() < 0.1 && !IsRipe)
{
IsRipe = true;
OnRipen?.Invoke(this, new(IsRipe));
}
}
}

static class TreeFarm
{
public static Tree AddNotifier<T>(this Tree tree) where T : IRipenNotifier
{
tree.OnRipen += T.OnNotify;
return tree;
}

public static Tree AddHarvester<T>(this Tree tree) where T : IHarvester
{
tree.OnRipen += T.OnHarvest;
return tree;
}
}

interface IRipenNotifier
{
static abstract void OnNotify(object? sender, RipenEventArgs e);
}

class MyRipenNotifier : IRipenNotifier
{
public static void OnNotify(object? sender, RipenEventArgs e)
{
if (sender is not Tree tree)
return;

Console.WriteLine("Tree is now ripe!");
}
}

interface IHarvester
{
static abstract void OnHarvest(object? sender, RipenEventArgs e);
}

class MyTreeHarvester : IHarvester
{
public static void OnHarvest(object? sender, RipenEventArgs e)
{
if (sender is not Tree tree)
return;

Console.WriteLine("Harvesting that tree!");
tree.IsRipe = false;
}
}

class RipenEventArgs : EventArgs
{
public RipenEventArgs(bool isRipe)
{
Ripened = isRipe;
}

public bool Ripened { get; }
}
i think this requires .net 7 and preview lang though
Pobiega
Pobiega2y ago
yup, static abstracts but its a nice example of how something like this could look in a "real" project given that your event handlers are simple generics + extension methods = superpowers
BigggMoustache
so on tree death I'd just Ripened = null and that'd fix it?
Pobiega
Pobiega2y ago
for that case, yes there is a reverse thou, which is when the publisher (event source) lives longer than the subscriber if the subscriber upon death doesnt unsub itself
BigggMoustache
I was just about to ask that lol and @Ero I'll ask about your thing in a moment. Tyvm
ero
ero2y ago
I'ma go to sleep
BigggMoustache
❤️ okay. I appreciate the help bud.
Pobiega
Pobiega2y ago
its only really a problem when the lifetimes of the subscriber and the publisher differ greatly
BigggMoustache
have a good night. why would sub need to keep reference of event around if source is gone? to use the data in the arg?
Pobiega
Pobiega2y ago
no its the publisher that knows about the subscribers
BigggMoustache
oh okay
ero
ero2y ago
Yeah the order of execution here isn't really sensical
Pobiega
Pobiega2y ago
once the source dies, the handler will not be invoked
ero
ero2y ago
A tree shouldn't have a harvester
Pobiega
Pobiega2y ago
because the source is dead
BigggMoustache
yeah i get that
ero
ero2y ago
A harvester should be assigned the task of harvesting a number of trees
BigggMoustache
NICE. getting pinned I'm looking at what Ero shared I've only used generics a couple times. This is super cool. What is being extended here though? Or were you just saying extensionmethods are based af? lol
ero
ero2y ago
Tree in TreeFarm
Pobiega
Pobiega2y ago
extension methods are just static methods that "pretend" to be part of something else so intead of doing TreeFarm.AddNotifier(tree, ...) you can call tree.AddNotifier(...) its the exact same thing
BigggMoustache
Oh yeah okay
Pobiega
Pobiega2y ago
they are very common in modern C#
BigggMoustache
yeah everything in there is new to me so it's more than a bit confusing xD
ero
ero2y ago
I guess they don't need to be extensions
BigggMoustache
Generics, Events, and ExtensionMethods are things I've only done a couple times
ero
ero2y ago
Could just be part of tree
BigggMoustache
I'm gonna copy it and step through it
Pobiega
Pobiega2y ago
well you don't need to worry about this right now
BigggMoustache
oh well okay hahahaha
Pobiega
Pobiega2y ago
your example is a bit contrived, and very simple
BigggMoustache
ya
ero
ero2y ago
You probably can't copy it anyway
BigggMoustache
Oh 😭 lol
ero
ero2y ago
Unless you have a preview version of .net 7 downloaded and bs 2022 preview installed vs
BigggMoustache
That's a no lol
ero
ero2y ago
Then you'll have to wait until November lol
BigggMoustache
So what you folks are telling me is I did an okay job with this exercise and to move onto the next one? xD
Pobiega
Pobiega2y ago
I refrain from commenting 😛
BigggMoustache
👀
ero
ero2y ago
Eh, it works, right?
BigggMoustache
lollllllllll
ero
ero2y ago
It's not something you would ever design in a real app
Pobiega
Pobiega2y ago
Like, we don't know the exercise, only your code and as said, its a contrived example that is too simple to be realistic
ero
ero2y ago
I don't think there was a task
BigggMoustache
Objectives:
• Make a new project that includes the above code.
• Add a Ripened event to the CharberryTree class that is raised when the tree ripens.
• Make a Notifier class that knows about the tree (Hint: perhaps pass it in as a constructor
parameter) and subscribes to its Ripened event. Attach a handler that displays something like “A
charberry fruit has ripened!” to the console window.
• Make a Harvester class that knows about the tree (Hint: like the notifier, this could be passed as
a constructor parameter) and subscribes to its Ripened event. Attach a handler that sets the tree’s
Ripe property back to false.
• Update your main method to create a tree, notifier, and harvester, and get them to work together
to grow, notify, and harvest forever
Objectives:
• Make a new project that includes the above code.
• Add a Ripened event to the CharberryTree class that is raised when the tree ripens.
• Make a Notifier class that knows about the tree (Hint: perhaps pass it in as a constructor
parameter) and subscribes to its Ripened event. Attach a handler that displays something like “A
charberry fruit has ripened!” to the console window.
• Make a Harvester class that knows about the tree (Hint: like the notifier, this could be passed as
a constructor parameter) and subscribes to its Ripened event. Attach a handler that sets the tree’s
Ripe property back to false.
• Update your main method to create a tree, notifier, and harvester, and get them to work together
to grow, notify, and harvest forever
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();
public class CharberryTree
{
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
}
}
}
CharberryTree tree = new CharberryTree();
while (true)
tree.MaybeGrow();
public class CharberryTree
{
private Random _random = new Random();
public bool Ripe { get; set; }
public void MaybeGrow()
{
// Only a tiny chance of ripening each time, but we try a lot!
if (_random.NextDouble() < 0.00000001 && !Ripe)
{
Ripe = true;
}
}
}
Pobiega
Pobiega2y ago
alright given that description, yeah you did alright
BigggMoustache
NICE.
ero
ero2y ago
Yeah that's fine
Pobiega
Pobiega2y ago
I'd remove the Tree prop from the harvester/notifier but other than that, 👍
BigggMoustache
heck yes
ero
ero2y ago
If anything make it a private field, but if it's not used, remove it completely
BigggMoustache
ya it's not used now I had confusion about sender before which is why I had it probably still have confusion about sender but that's okay lol ❤️ thanks again Ero and Pobiega. Maybbe someday your discord handles will be references in my resume. xD
Pobiega
Pobiega2y ago
wtf :d don't
ero
ero2y ago
And isn't that truly why we're here
Pobiega
Pobiega2y ago
thats not what a reference is, lol
ero
ero2y ago
*referenced
BigggMoustache
"Give three references of people you know who can attest the quality of your work" Uhhh.. @Pobiega#2671, uhhh...
Pobiega
Pobiega2y ago
yeah, nah
BigggMoustache
lmao I know I know I kid.
Pobiega
Pobiega2y ago
references on CVs need to be actual people you've worked with.
BigggMoustache
I'm just saying you folks are a large part of what's going to get me through this. ❤️
ero
ero2y ago
Hey I'm as real as any other person thank you
BigggMoustache
LOL
Pobiega
Pobiega2y ago
I'm just a shared figment of both your imaginations
BigggMoustache
DUDE POBIEGA, I HAVE AN INTERESTING TWIST ON THAT THAT HAS IMPLICATIONS ABOUT THE NATURE OF BEING sorry, i dig philosophy though To Hegel, 'being' is the product of a social function, that is 'consciousness reflecting on consciousness' the object, that is subject, become subject of the subject.
Pobiega
Pobiega2y ago
/close?
BigggMoustache
LOL okay fine.
Pobiega
Pobiega2y ago
:d
ero
ero2y ago
I was about to send that lmfao
BigggMoustache
haha ty again.
Accord
Accord2y ago
✅ This post has been marked as answered!