I have a form with two RichTextBoxs on them, I want to raise an event in one to trigger an event on the form which updates something in the other rtb.
Where must I define the EventArgs extended class?
What is and where must my delegate be? Is this the function that is called from the event being raised?
Do these three classes (rtb1, rtb2, form1) need to be in the same namespace?
Rather than defining your own delegate you can almost always use the generic EventArgs class, or the Action class as the delegate for your events. When using one of these you won't need to define any delegate at all, they are already defined by the library.
The delegate is not the function being called, it merely defines the signature of all methods that are called by the event.
The three classes do not need to be in the same namespace, no. In fact, as a general rule, there are almost no situations (none that I can think of anyway) which would force you to have classes in the same namespace for them to do something.
Related
We have a class which derives from FrameworkElement and which is not under our control.
This class registers eventhandlers in the OnInitialize method.
What is the pattern to properly clean this class up, since FrameworkElement does not provide a counterpart to OnInitialize?
It remains rooted since no part of it removes the EventHandler causing a leak.
There's no such thing as "deinitializing". Initialization methods are used when it isn't possible to fully initialize an object in its constructor, because it depends on data that isn't available during construction. In these cases construction is broken in two phases: the first step is executed in a parameterless constructor, the second in an initialization method, like OnInitialize, after the external data becomes available
What you describe is object disposal, which is performed by calling an object's Dispose method. A well-written class should clean up its data, release any external resources and release any event handlers.
Visual elements typically have another step in their lifecycle, handled by the OnLoad/OnUnload methods. The Load step occurs when an element is actually placed in XAML view and connected to the other UI elements. Event handlers should be registered in the OnLoad method and removed in the OnUnload method.
If the element has no Dispose method , you may be able to raise the Unload event to force the cleanup, although this is a bit of a hack:
control.RaiseEvent(new RoutedEventArgs(FrameworkElement.UloadedEvent))
Use weak event pattern. In particular, subscribe to events using WeakEventManager implementations (or, better, its generic version WeakEventManager<TEventSource, TEventArgs>, if .NET 4.5 is an option).
Use Dispatcher.ShutdownStarted event.
Add this to the constructor:
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
And add your cleanup code to this method:
private void Dispatcher_ShutdownStarted(object sender, EventArgs e)
{
Stop();
}
I have a question regarding raising base class events. I am currently reading the MSDN C# Programming Guide and I cannot understand one thing from the below article:
http://msdn.microsoft.com/en-us/library/vstudio/hy3sefw3.aspx
public void AddShape(Shape s)
{
_list.Add(s);
s.ShapeChanged += HandleShapeChanged;
}
OK, so we are registering the delegate with an event and we'll be calling the private method HandleShapeChanged when the event is raised.
public void Update(double d)
{
radius = d;
area = Math.PI * radius * radius;
OnShapeChanged(new ShapeEventArgs(area));
}
protected override void OnShapeChanged(ShapeEventArgs e)
{
base.OnShapeChanged(e);
}
And here we are calling the base class method OnShapeChanged, which, in success, will fire the event. But the event is based in the Shape class, so how can it access the method HandleShapeChanged, which is private?
I have just started to learn the language, so please bear with me. My understanding of this example may be well off target.
I see the way you are thinking, it feels like another class is calling a private method. But no, the best way to think about it is that access rules are checked when the delegate is created, not when it is invoked. And that of course is no problem, the class can access its own private method.
Also checking it when it is invoked would be lousy, that would require making the event handler public. And handling events is almost always a very private implementation detail of a class that no external code should ever be allowed to call directly. Since that would mean that you couldn't be sure in your event handler that it was actually the event that triggered the call. That's bad, very bad.
The event is invoking the delegate. It doesn't matter what code is in the delegate. Since the method is not being invoked explicitly, access rules do not apply.
Note also that this has nothing to do with the fact that the event is in a base class. The exact same thing would happen even if the event were in a totally unrelated class.
public delegate void SecondChangedHandler(
object clock,
TimeInfoEventArgs timeInformation);
public event SecondChangedHandler SecondChanged;
I have written a clock based on this article.
Now if i remove the event keyword i get the same result, so what does event really do?
It's compiled differently. It makes it so someone can't do
mySecondChangedHandler.SecondChanged = SomeMethod(...); //overwrite
mySecondChangedHandler.SecondChanged(...); //invoke
but only
mySecondChangedHandler.SecondChanged += SomeMethod(...);
mySecondChangedHandler.SecondChanged -= SomeMethod(...);
The event keyword creates a private delegate field, and a pair of public event accessors called add_EventName and remove_EventName. (details)
This means that writing EventName inside the class returns the delegate instance, allowing you to call or inspect the event handlers.
Outside the class, EventName doesn't really exist; all you can do is write EventName += something and EventName -= something, and the compiler will convert it into calls to the accessors. (like a property)
For more details, see this series of blog posts.
The event keyword does two things
It supplied permissions. Only the class can raise the event, however any external method can invoke the raw delegate
It provides metadata which can be used for designers and the like
The event keywords means only methods on the instance that hosts the SecondChanged field can invoke it. External attempts will fail.
The event keyword creates a pair of accessors for a delegate. These are effectively two methods (add and remove) that are called when you subscribe or unsubscribe from the event.
In your case, you're creating a "field-like event". The compiler makes a delegate behind the scenes, and allows you to subscribe and unsubscribe from it's invocation list. This means that you have all of the functionality of a delegate, but you're restricting access so that that outside world can "handle" the event, but not raise the event (invoke the delegate).
You can, however, also explicitly create your own accessors for an event, and these can do other things (though that's not typically recommended unless there is a good reason to do so).
Quote from C# lang reference about keyword event,
Events are a special kind of multicast delegate that can only be invoked from within the class or struct where they are declared (the publisher class).
I have an event Load
public delegate void OnLoad(int i);
public event OnLoad Load;
I subscribe to it with a method:
public void Go()
{
Load += (x) => { };
}
Is it possible to retrieve this method using reflection? How?
In this particular case you could, with reflection. However, in general, you can't. Events encapsulate the idea of subscribers subscribing and unsubscribing - and that's all. A subscriber isn't meant to find out what other subscribers there are.
A field-like event as you've just shown is simply backed by a field of the relevant delegate type, with autogenerated add/remove handlers which just use the field. However, there's nothing to say they have to be implemented like that. For example, an event can store its subscribers in an EventHandlerList, which is efficient if you have several events in a class and only a few of them are likely to be subscribed to.
Now I suppose you could try to find the body of the "add" handler, decompile it and work out how the event handlers are being stored, and fetch them that way... but please don't. You're creating a lot of work, just to break encapsulation. Just redesign your code so that you don't need to do this.
EDIT: I've been assuming that you're talking about getting the subscribers from outside the class declaring the event. If you're inside the class declaring the event, then it's easy, because you know how the event is being stored.
At that point, the problem goes from "fetching the subscribers of an event" to "fetching the individual delegates making up a multicast delegate" - and that's easy. As others have said, you can call Delegate.GetInvocationList to get an array of delegates... and then use the Delegate.Method property to get the method that that particular delegate targets.
Now, let's look again at your subscription code:
public void Go()
{
Load += (x) => { };
}
The method that's used to create the delegate here isn't Go... it's a method created by the C# compiler. It will have an "unspeakable name" (usually with angle brackets) so will look something like this:
[CompilerGenerated]
private static void <Go>b__0(int x)
{
}
Now, is that actually what you want to retrieve? Or were you really looking to find out which method performed the subscription, rather than which method was used as the subscribed handler?
If you call Load.GetInvocationList() you will be handed back an array of Delegate types. From the these types, you can access the MethodInfo.
You could use the GetInvocationList method which will give you all the subscribers.
In C#, I find myself occasionally wanting to register a method for an event in the middle of a dispatch of that same event. For example, if I have a class that transitions states based on successive dispatches of the same event, I might want the first state's handler to unregister itself and register the second handler. However, I don't want the second handler to be dispatched until the next time the event is fired.
The good news is that it looks like the Microsoft implementation of C# behaves exactly this way. The event registration syntax sugar gets replaced with a call to System.Delegate.Combine, which just concatenates the current invocation list and the new method into a separate list and assigns it to the event property. This gives me exactly the behavior I want.
So, my question is: is this guaranteed behavior by the language standard? I like to be able to run my C# code on other platforms under mono and generally want to make sure I'm not making assumptions about the language standard based on its implementation.
I couldn't find any definitive information on MSDN.
If you'd like a specific example of what I'm talking about, here's an example:
delegate void TestDelegate();
static event TestDelegate TestEvent;
static void Main (string[] args) {
TestEvent += TestDelegateInstanceFirst;
TestEvent();
TestEvent();
}
static void TestDelegateInstanceFirst () {
Console.WriteLine("First");
TestEvent += TestDelegateInstanceSecond;
}
static void TestDelegateInstanceSecond () {
Console.WriteLine("Second");
}
At least on Windows, the output is:
First
First
Second
Yes, it's guaranteed.
From the unified C# 3.0 spec, section 15.1:
However, when two non-null delegate
instances are combined, their
invocation lists are concatenated—in
the order left operand then right
operand—to form a new invocation list,
which contains two or more entries.
Note the "new invocation list". And again in section 15.3:
Once instantiated, delegate instances
always refer to the same target object
and method. Remember, when two
delegates are combined, or one is
removed from another, a new delegate
results with its own invocation list;
the invocation lists of the delegates
combined or removed remain unchanged.
Finally, MSDN for System.Delegate states:
Delegates are immutable; once created,
the invocation list of a delegate does
not change.
I suspect there's something in the CLI spec - I'll check if you'd like, but hopefully these three have given you enough confidence :)