Casting and Types Question - c#

I am bubbling events in my application and so therefore using the bubble events method. As this method handles all sorts of bubbled events their is a switch or if statement within it to determine what sort of event we're dealing with. I was wondering if I could get around this by creating different versions of the event args class. So let me explain, say I have two types of event that are handled differently called X and Y, I create new event args classes for these two events as they store different types of info.
public class EventsArgsX : EventsArgs
public class EventsArgsY : EventsArgs
then when I RaiseBubbleEvent from somewhere in my application I can pass either of the two event arg based types, so..
EventArgsX foox = new EventArgsX();
RaiseBubbleEvent(null,foox);
or
EventArgsY fooy = new EventArgsY();
RaiseBubbleEvent(null,fooy);
then the OnBubbleEvent method picks this up, who's signature is
override OnBubbleEvent(object source, EventArgs e)
now i cant overload this method as its overriden in the first place, so what I thought I could do was have another method with overloads in it to handle this, so
protected override OnBubbleEvent(object source, EventArgs e)
{
DoStuff(e);
}
private void DoStuff(EventArgsY args)
{}
private void DoStuff(EventArgsX args)
{}
but of course the problem is that EventArgs e in the OnBubbleEvent method is of type EventArgs at the time of calling. However we know its not. So how would i case it back to its actual type in order for the method call to work?
Many thanks, hope you can help me with this, its seems really easy like a might be missing something or that it just cant be done
any ideas??

It's simple:
protected override OnBubbleEvent(object source, EventArgs e)
{
if(e is EventArgsX)
DoStuff((EventArgsX)e);
else if(e is EventArgsY)
DoStuff((EventArgsY)e);
}
This, being KISS, is not very extensible. If you're planning on adding more event types, you can try double dispatch:
public abstract class EventArgsBase : EventArgs
{
public abstract void Bubble(IEventBubbler eb);
}
public interface IEventBubbler
{
Bubble(EventArgsX ex);
Bubble(EventArgsY ey);
}
public class EventArgsX : EventArgsBase
{
public virtual void Bubble(IEventBubbler eb)
{
eb.Bubble(this);
}
}
public class EventArgsY : EventArgsBase
{
public virtual void Bubble(IEventBubbler eb)
{
eb.Bubble(this);
}
}

Related

Subscribing to an event of a base class

I am trying to subscribe to an event that is in my base class but the method in my derived class doesn't seem to trigger whenever that event is triggered. Sample code is below.
public abstract class BaseClass
{
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler Event;
protected virtual void OnEvent(EventArgs ea)
{
if (this.Event!= null)
{
this.Event(null, ea);
}
}
}
public partial class DerivedClass : BaseClass
{
protected override void OnInit(EventArgs e)
{
base.Event+= DoSomething;
}
private void DoSomething(object sender, EventArgs e)
{
//Do Something here.
}
}
BaseClass.OnEvent is called in another control that has the same base class and the derived class where I want to subscribe to is inside another control. Is this possible?
BaseClass.OnEvent is called in another control that has the same base class and the derived class where I want to subscribe to is inside another control. Is this possible?
If you have a derived class and you have two instances of this derived class, the method of instance one won't be called if anything happens in instance two.
In your case you even have two different derived classes, sharing one base class and at runtime you have at least one instance of each derived class, which means, there is no communication between these two.
If you need to link two instances together you have to do something like this:
var instanceOne = new DerivedClassOne();
var instanceTwo = new DerivedClassTwo();
// When something in one happens, let two know:
instanceOne.OnEvent += (sender, e) => instanceTwo.ReactOnOtherChange();

How to perform a cross-hierarchy call in C#

I am working on a simple GUI framework, and I faced a problem calling protected virtual methods.
Here is the IKeyboardInputListenerService interface of service I use to receive keyboard events and the Control class that represents a base for all my GUI controls. The internal method is the one being refactored.
public interface IKeyboardInputListenerService
{
event EventHandler<KeyboardEventArgs> KeyPressed;
}
public abstract class Control
{
public IKeyboardInputListenerService KeyboardInputListenerService { get; }
protected Control(IKeyboardInputListenerService keyboardInputListenerService) =>
KeyboardInputListenerService = keyboardInputListenerService;
public event EventHandler<KeyboardEventArgs> KeyPressed;
/* protected */ internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{
if (enabled && visible && focused && !args.Suppressed)
{
KeyPressed?.Invoke(sender, args);
args.Suppressed = true;
}
}
public void Activate() =>
KeyboardInputListenerService.KeyPressed += new EventHandler<KeyboardEventArgs>(OnKeyPressed);
}
I also created a ContainerControl class that is supposed to contain child controls(like the Panel or GroupBox in Windows Forms) and that overrides the virtual method:
public abstract class ContainerControl : Control
{
private readonly ObservableCollection<Control> controls;
protected ContainerControl(IKeyboardInputListenerService keyboardInputListenerService)
: base(keyboardInputListenerService) =>
controls = new ObservableCollection<Control>();
/* protected */ internal override void OnKeyPressed(object sender, KeyboardEventArgs args)
{
foreach (Control control in controls)
control.OnKeyPressed(sender, args);
base.OnKeyPressed(sender, args);
}
}
Problem is, I cannot decide which modifier to use for methods such as OnKeyPressed. I wanted to make them protected, but it causes a compiler error:
Error CS1540 Cannot access protected member 'Control.OnKeyPressed(object, KeyboardEventArgs)' via a qualifier of type 'Control'; the qualifier must be of type 'ContainerControl' (or derived from it)
I can make them public, but I do not really think it is a good idea, because there is no reason for it except for resolving the problem caused by a cross-hierarchy call. I made them internal, but there is also a drawback: if anyone will want to create a user control, they will not be able to receive events, so the control will be useless.
The question is how to get access from a derived class to virtual methods of the base class without making the methods public-accessed.
Use protected internal
protected internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{ ... }
The documentation says:
protected internal The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly.

c# How to call and handle a custom generic event arg?

hoping someone could lend a hand here. I have a custom even argument that contains a collection. I can build the event arg but I cannot implement it. I want to put the event handler in the base class and override it in my derived classes.
The even arg:
public class ImportEventArgs<T> : EventArgs
{
public IEnumerable<T> Data { get; }
public ImportEventArgs(IEnumerable<T> data)
{
Data = data;
}
}
The part in my base class that is not resolving:
public virtual void EventHandler<EventArgs> ImportComplete;
The override:
override void EventHandler<WellPathImportEventArgs<WellPath>> ImportComplete;
I figured this is not the correct way of doing this, could anyone point me in the right direction?
You cannot change a method's signature when you override it.
However, since all event args derive from the same type, you don't have to. Just keep the existing method definition and cast the argument, like this:
public override void ImportComplete(object sender, EventArgs e)
{
var myEventArgs = e as ImportEventArgs<WellPath>;
if (myEventArgs != null)
{
foreach (var item in myEventArgs.Data)
{
//Your code here
}
}
}

Delegates and Events with multiple classes

This has taken me quite a few days to develop a demo of communicating between classes with delegates and events. I would like to know if this is the best practices way of accomplishing this passing of data between classes or not. If there is a better method please let me know. Specifically, if something happens in a subclass how do you get it back to the main class. This would be particularly useful when doing n-tier architecture by separating out the User Interface from the Business Logic Level, and the Data Access Level.
I have a form that has 3 text boxes: tb1, tb2, and tbAnswer.
I also have a button that says "Add" and it is just button1.
The main form is:
namespace DelegateTest
{
public delegate void ShowMessage(object sender, Form1.AnswerEventArgs e);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void a_OnShowMessage(object sender, AnswerEventArgs e)
{
tbAnswer.Text = e.answer;
}
public class AnswerEventArgs :EventArgs
{
public string answer { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
AddClass a = new AddClass();
a.OnShowMessage += new ShowMessage(a_OnShowMessage);
a.AddMe(Convert.ToInt16(tb1.Text), Convert.ToInt16(tb2.Text));
}
}
}
and the subform called AddClass.cs is:
namespace DelegateTest
{
class AddClass
{
public event ShowMessage OnShowMessage;
public void AddMe(int a, int b)
{
Form1.AnswerEventArgs e = new Form1.AnswerEventArgs();
e.answer = (a+b).ToString();
OnShowMessage(this, e);
}
}
}
Your approach is sound except for two details.
First, a NullPointerException will occur if your event is raised before any handlers are added. You can get around this in one of two ways.
1) Make a local copy (to avoid race condition) and check it for null:
var showMessage = OnShowMessage;
if (showMessage != null)
{
showMessage(this, e);
}
2) Initialize your event to an empty delegate:
public event ShowMessage OnShowMessage = delegate { };
Second, you do not need to declare your own delegate in this case. You can simply create a standard EventHandler with your own event args:
public event EventHandler<Form1.AnswerEventArgs> OnShowMessage = delegate { };
See How to: Publish Events that Conform to .NET Framework Guidelines for more information.

Why events can't be used in the same way in derived classes as in the base class in C#?

In following code, I want to extend the behaviour of a class by deriving/subclassing it, and make use of an event of the base class:
public class A
{
public event EventHandler SomeEvent;
public void someMethod()
{
if(SomeEvent != null) SomeEvent(this, someArgs);
}
}
public class B : A
{
public void someOtherMethod()
{
if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible?
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -=
//(except when used from within the type 'A')
}
}
Why isn't it possible?
And what is the common solution for this kind of situation?
Others have explained how to get round the issue, but not why it's coming up.
When you declare a public field-like event, the compiler creates a public event, and a private field. Within the same class (or nested classes) you can get at the field directly, e.g. to invoke all the handlers. From other classes, you only see the event, which only allows subscription and unsubscription.
The standard practice here is to have a protected virtual method OnSomeEvent on your base class, then call that method in derived classes. Also, for threading reasons you will want to keep a reference to the handler before checking null and calling it.
For an explanation of the why read Jon Skeet's answer or the C# specification which describes how the compiler automatically creates a private field.
Here is one possible work around.
public class A
{
public event EventHandler SomeEvent;
public void someMethod()
{
OnSomeEvent();
}
protected void OnSomeEvent()
{
EventHandler handler = SomeEvent;
if(handler != null)
handler(this, someArgs);
}
}
public class B : A
{
public void someOtherMethod()
{
OnSomeEvent();
}
}
Edit: Updated code based upon Framework Design Guidelines section 5.4 and reminders by others.
Todd's answer is correct. Often you will see this implemented throughout the .NET framework as OnXXX(EventArgs) methods:
public class Foo
{
public event EventHandler Click;
protected virtual void OnClick(EventArgs e)
{
var click = Click;
if (click != null)
click(this, e);
}
}
I strongly encourage you to consider the EventArgs<T>/EventHandler<T> pattern before you find yourself making all manner of CustomEventArgs/CustomEventHandler for raising events.
The reason the original code doesn't work is because you need to have access to the event's delegate in order to raise it, and C# keeps this delegate private.
Events in C# are represented publicly by a pair of methods, add_SomeEvent and remove_SomeEvent, which is why you can subscribe to an event from outside the class, but not raise it.
My answer would be that you shouldn't have to do this.
C# nicely enforces Only the type declaring/publishing the event should fire/raise it.
If the base class trusted derivations to have the capability to raise its events, the creator would expose protected methods to do that. If they don't exist, its a good hint that you probably shouldn't do this.
My contrived example as to how different the world would be if derived types were allowed to raise events in their ancestors. Note: this is not valid C# code.. (yet..)
public class GoodVigilante
{
public event EventHandler LaunchMissiles;
public void Evaluate()
{
Action a = DetermineCourseOfAction(); // method that evaluates every possible
// non-violent solution before resorting to 'Unleashing the fury'
if (null != a)
{ a.Do(); }
else
{ if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); }
}
virtual protected string WhatsTheTime()
{ return DateTime.Now.ToString(); }
....
}
public class TriggerHappy : GoodVigilante
{
protected override string WhatsTheTime()
{
if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty);
}
}
// client code
GoodVigilante a = new GoodVigilante();
a.LaunchMissiles += new EventHandler(FireAway);
GoodVigilante b = new TriggerHappy(); // rogue/imposter
b.LaunchMissiles += new EventHandler(FireAway);
private void FireAway(object sender, EventArgs e)
{
// nuke 'em
}
Wrap it with a protected virtual On... method:
public class BaseClass
{
public event EventHandler<MyArgs> SomeEvent;
protected virtual void OnSomeEvent()
{
if(SomeEvent!= null)
SomeEvent(this, new MyArgs(...) );
}
}
Then override this in a derived class
public class DerivedClass : BaseClass
{
protected override void OnSomeEvent()
{
//do something
base.OnSomeEvent();
}
}
You'll set this pattern all over .Net - all form and web controls follow it.
Do not use the prefix Raise... - this is not consistent with MS's standards and can cause confusion elsewhere.

Categories

Resources