I am new to C# and programming in general and am trying to figure out how to use events. Previously I have been programming with ActionScript3, and there events are a special class that you inherit from if you want to create your own events, and then that event can be called by any other class.
With C# I have tried to do something similar, like so:
public class EventManager
{
public delegate void TempDelegate();
public static event TempDelegate eSomeEvent;
}
public class SomeOtherClass
{
//doing some stuff, then:
if (EventManager.eSomeEvent != null)
{
EventManager.eSomeEvent();
}
}
This gives me a compiler error CS0070: The event 'EventManager.eSomeEvent' can only appear on the left hand side of += or -= (except when used from within the type 'EventManager')
The information about this error over on the msdn indicates that I should use += instead of trying to call the event, but I don't really understand this. I'm not trying to subscribe anything from SomeOtherClass to the event delegate, I am just trying to call this event so that it starts executing those functions that are already subscribed to that event.
So is it possible to do it this way? If not, is it at all possible to call an event that is of one class, from another class? I simply wish to reuse certain events in my classes rather than creating many similar ones in multiple classes.
Any advice with this would be greatly appreciated!
You can wrap the event call in a public method and use that from your other classes.
public void OnSomeEvent()
{
var handler = eSomeEvent;
if (handler != null) handler(this, null);
}
However you might want to look at the design again, if you are really sure the event should be on a different class than the one triggering it.
Well, the typical solution is to put eSomeEvent invocation into the EventManager class
public class EventManager
{
public delegate void TempDelegate();
public static event TempDelegate eSomeEvent;
// Not thread safe as well as your code
// May be internal, not public is better (if SomeOtherClass is in the same namespace)
public static void PerformSomeEvent() {
if (!Object.ReferenceEquals(null, eSomeEvent))
eSomeEvent(); // <- You can do it here
}
}
public class SomeOtherClass
{
//doing some stuff, then:
EventManager.PerformSomeEvent();
}
Related
In my project, I need to implement an event that fires when a popup or something similar is pulled up so that I can close anything that needs to hide behind it for whatever reason.
For context, I have 3 files in play here, MainShell which fires the event, IShell which is an interface that MainShell implements and defines the event, and Reports which listens for the event. I could have put the event in MainShell and made everything simpler, however the project references would become circular if I did that. That's just what I have to work with. I can, however, refer to the IShell interface that defines the functions MainShell uses. Unfortunately, it seems attempting to use an event from a derived class/interface causes the implementation to become very complicated and picky for some reason.
In my interface file:
public class ModuleShownEventArgs : EventArgs { }
public delegate void ModuleShownEventHandler(object sender, ModuleShownEventArgs e);
public interface IShell {
event ModuleShownEventHandler ModuleShown;
... }
In my listening class:
public Reports() {
...
Container.Shell.ModuleShown += Shell_ModuleShown;
... }
private void Shell_ModuleShown(object sender, ModuleShownEventArgs e) {}
In my event firing class:
event ModuleShownEventHandler IShell.ModuleShown
{
add
{
((IShell)this).ModuleShown += value;
}
remove
{
((IShell)this).ModuleShown -= value;
}
}
public void OnModuleShown()
{
ModuleShownEventHandler handler = ((IShell)this).ModuleShown;
if (handler != null)
handler(this, new ModuleShownEventArgs());
}
I've managed to stop most of the compiler's complaints, but I'm down to one problem: there's an error where I assign handler = ModuleShown,
the event 'IShell.ModuleShown' can only appear on the left hand side
of += or -=
This prevents me from easily comparing my event to null for checking, and prevents me from firing my event at all.
Questions
How can I get this to work? Why can't I fire my event? Why does defining events change so drastically when they come from a base class/interface? Keep in mind that this project is quite large and I've only started working on it recently, so I can't make sweeping structural changes to it.
I am using Visual Studio 2013, and my project's .NET Framework version is 4.0.
The issue is that you are using explicit interface implementation. You should be using implicit instead.
public class Shell : IShell
{
public event ModuleShownEventHandler ModuleShown;
public void OnModuleShown()
{
ModuleShownEventHandler handler = ModuleShown;
if (handler != null)
{
handler(this, new ModuleShownEventArgs());
}
}
}
https://msdn.microsoft.com/en-us/library/ms173157.aspx
Ideally you will be passing around the instance that implements IShell as the interface if your concern was to hide the event from the Shell implementation. Typically, you use explicit interface implementations when you do not want your class to publicly expose an interface specific member.
I hope this helps.
It should look something like this instead.
public void OnModuleShown()
{
if (((IShell)this).ModuleShown != null);
(((IShell)this).ModuleShown)(this, new ModuleShownEventArgs());
}
Is that possible somehow to trigger an event which belongs another class in C#, such:
class foo
{
public delegate void myEvntHandler();
public event myEvntHandler onTesting;
.
.
.
}
class Main
{
public static void Main()
{
foo obj = new foo();
...
obj.onTesting.Invoke();
}
}
on this sample I mean: obj.onTesting.Invoke();
No you can't invoke it directly from another class. That's the point of events (Encapsulation).
You need a helper method
class foo
{
public delegate void myEvntHandler();
public event myEvntHandler onTesting;
public void RaiseEvent()
{
if(onTesting !=null)
onTesting ();
}
}
Then call RaiseEvent method instead
class Main
{
public static void Main()
{
foo obj = new foo();
...
obj.RaiseEvent();
}
}
If you need to invoke it the way you did, just remove the event keyword and use a delegate. Which doesn't prevent you form doing so.(I don't recommend it)
No. The whole purpose of events is to wrap a delegate while explicitly prohibiting all access to it other than adding/removing an event handler. The event keyword is there specifically to prevent any class other than the class that declares the event from invoking it.
Short answer: No.
Longer answer: under the hood there no such thing as an "event" unlike delegates which are real objects. event is just a convenience for two methods (to add and remove handlers) and a private hidden field of type myEvntHandler.
Logically it makes no sense to raise an event from outside the class: The whole point of an event is that it is raised by the class when the class detects some trigger.
If you just want it to raise an event in order to test another class that has added a handler then the correct way to go is to:
move the event to an interface
implement the interface in your real class
create a test class that also implements the interface and add your "RaiseEvent" method to that.
Inject the interface into your unit under test
The class hierarchy and the code is like the below:
And how to call GetInvocationList() to see how many functions have been bound to the Added delegation in the Main function?
namespace Test
{
public class Program
{
public class SelectSet
{
public event SelectSet.AddedEventHandler Added;
public delegate void AddedEventHandler(object BusinessObject);
}
public class C1
{
public static SelectSet SelectSet;
}
static void Main()
{
}
}
}
And how to call GetInvocationList() to see how many functions have been binded to the Added delegation in the Main function?
You can't (at least without reflection), and you shouldn't. The point of exposing an event is to only expose functionality to add or remove handlers. So it's a bit like your code was written like this:
public class SelectSet
{
// Private fields here
public void AddAddedHandler(SelectSet.AddedEventHandler handler)
{
// Implement using private fields
}
public void RemoveAddedHandler(SelectSet.AddedEventHandler handler)
{
// Implement using private fields
}
public delegate void AddedEventHandler(object BusinessObject);
}
If you look at that class, it's pretty obvious that from the outside you won't be able to find out what handlers there are. Just because you're declaring a field-like event doesn't give the outside world access to your field.
You can expose the handlers explicitly if you want, of course - but it's rarely a good idea.
See my article on events and delegates for more information.
In VB at least, the delegate is available as Private <EventName>Event, so you could retrieve AddedEvent.GetInvocationList directly from within the class, or using reflection from outside.
I have a class that instantiates two classes which implement interfaces. I want one class to notify another class that something is OK. I could do it with an Action and then use private variables in the class but wondered if there was a direct way of doing it with properties so that when a property's value changes it updates a property on another class.
For example:
public class MyClass
{
public ILogger Logger {get;set;}
public ILogic Logic {get;set;}
private Form MyWinform;
public void Setup()
{
MyWinform = new MyWinform();
MyWinform.StartBatch += Logger.CreateFile; //Create file when user presses start
//How can I set a property on ILogic to be AllOk once ILogger says so??
//I could use an Action so that once all is ok I call IDecidedAlOK in ILogger which
//can then assign a private bool variable inside the class
Logic.ItsOKMethodSoSetVariableToTrue = Logger.IDecidedAllOKMethod;
}
public void DataIn(string Value)
{
Logic.DataIn(Value);
}
public void CreateInstances()
{
Logger = new FileLogger();
Logic = new MyLogic();
}
}
public class MyLogic : ILogic
{
public void DataIn(string Value)
{
//I want to check that all is ok before I do anything
//if (!AllOK)
//return;
//Do stuff
}
}
Implement INotifyPropertyChanged interface and subscribe to PropertyChanged event
I feel like it might be a bit more conventional to have your ILogger interface expose something like a "FileCreated" or "Ready" event, and allow your application to handle that event in order to update the ILogic object (or do whatever else is necessary).
EDIT: my apologies, after re-reading the question, I think I misunderstood what you were asking for.
There isn't any "natural" object that does exactly what you're asking, but you could create an anonymous delegate (or lambda expression) for this purpose:
Action<bool> loggerResult = (value) => Logic.ItsOKMethodSoSetVariableToTrue = value;
A property internally consists of two private methods, a get_XXX and a set_XXX, so unless you want to fetch the MethodInfo of those methods and invoke them (which are again methods) you have no choice but to implement a method calling approach.
Subscribing to event (INotifyPropertyChanged or some custom one) is OK, so is the method to pass a lambda-setter, but in some cases it might be more convinient to use a shared context object (much like the shared memory concept):
class ConversationContext
{
public bool EverythingIsOK { get; set;}
}
This object is passed to all interested objects (ILogic and ILogger) and they operate directly on it, instead of some internal properties. If change notifications are required, Implement INotifyPropertyChanged on it.
One positive aspect of this approach is that you won't get tangled in repeatedly firing events triggering other events and so on. A single object will hold the current state and no recurrent updates are needed.
Again, this is just one of many options.
Is there a way to create some sort of interface that only allows the object to be accessible through events?
Can't you just define an interface with only events in it?
For instance:
interface IExample
{
event EventHandler Event1;
event EventHandler Event2;
}
class Obj : IExample
{
public event EventHandler Event1;
public event EventHandler Event2;
}
Usage:
IExample obj = new Obj();
obj.Event1 += Event1_Handler;
obj.Event2 += Event2_Handler;
Without further information, the best answer I have is that you would simply need to make sure that all of the members properties, functions, etc) are declared as private, except for the events, which would be public.
Although I have to admit, I'm stumped as to how this would eve be useful, and what would trigger a event if it's only accessible to it's events. It's like saying can you create a phone that you can't call, but can only hear the ring (the IncomingCall event).
A setup like this would expose only events to a client using the assembly:
interface ISomething {
event EventHandler MyEvent;
}
internal class MyClass : ISomething {
...
}
public ClassFactory {
public ISomething GetClass(){ // factory method
return new MyClass();
}
}
Or, if you need to restrict the use of this class in its own library as well you can do this:
public class MyClass : ISomething {
private MyClass(){} // private constructor
public ISomething GetClass(){ // factory method
return new MyClass();
}
}
Something like this may be combined with a singleton object if you just need to get its events as well, which can make sense if you simply want have a generic way to subscribe to that object's status events for example.
Be aware that any object to which a caller has access can have any of it's fields accessed through reflection.
If your question is focused on preventing people from accidentally invoking your object incorrectly, Matt B.'s answer is great.
If your question is focused on making it impossible for someone to maliciously access private fields of your object, that's not possible.