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
Related
I'm using the event-based design pattern and I want to know if there's a simple and clean way to make all classes that implement a certain interface also subscribe to a public static event declared on another class. I will try to express what I'm trying to do with a simple code:
public interface IPlayControl
{
void OnStart();
void OnStop();
}
public class ClassWithEvent : SomeOtherClass
{
//this event needs to be subscribed to OnStart/OnStop methods of IPlayControl
public static event Action ActionStarted, ActionStopped;
}
public class Subscriber : IPlayControl
{
public void OnStart() {}
public void OnStop() {}
}
I want all classes like Subscriber to automatically subscribe their OnStart and OnStop methods to those single static ActionStarted, ActionStopped events of the ClassWithEvent.
So instead of registering them everytime like:
ClassWithEvent.ActionStarted += OnStart;
ClassWithEvent.ActionStopped += OnStop;
I think it would be easier and look cleaner without adding these lines to every class.
I feel like it would be an unconventional way of doing this but should I do it manually by checking all types that implement this interface and subscribing their methods with a loop? I found this code on another post.
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
Thanks.
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();
}
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.
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.
OK, I've tried but I just don't get it.
I have two classes logger and class1.
I have a method called logger.Write(string) and method called class1.Execute().
Now in my application I want to have logger.Write(class1.ToString()) run when class1.Execute() is called.
I presume you need to add delegates and events, but I just can't get my head around this, been scratching it for a couple hours.
One thing, is that the logger and the class are in different namespaces and I don't want to update the class code for either of them if possible.
Well you certainly can't do it without changing code in either class (assuming you also don't want to change everywhere that calls class1.Execute) - at least not without some deep code-weaving/instrumentation magic. However, you can fairly easily add an event in Class1:
public class Class1
{
// TODO: Think of a better name :)
public event EventHandler ExecuteCalled = delegate {};
public void Execute()
{
ExecuteCalled(this, EventArgs.Empty);
// Do your normal stuff
}
}
The delegate{} bit is just to make sure that there's always at least a no-op event handler registered - it means you don't need to check for nullity.
You'd then hook it up by writing:
Class1 class1 = new Class1();
Logger logger = new Logger();
class1.ExecuteCalled += (sender, args) => logger.Write(sender.ToString());
(This is assuming you're using C# 3 so you have lambda expressions available to you - let me know if that's not the case.)
If Class1 implements an interface (say IFoo), you might want to write an implementation of the interface which wraps another implementation, and just logs before each call:
public sealed class LoggingFoo : IFoo
{
private readonly IFoo original;
private readonly IFoo logger;
public LoggingFoo(IFoo original, Logger logger)
{
// TODO: Check arguments for nullity
this.original = original;
this.logger = logger;
}
// Implement IFoo
public void Execute()
{
logger.Write("Calling Execute on {0}", original);
original.Execute();
}
}
Then just use that wrapper around a "real" implementation wherever you currently just use the implementation.
Can you pass an object parameter for logger and then just call the ToString on that? The proper ToString method will be called. If you don't want to change anything in logger or class1, then you could write an extension method and call that instead of calling class1.Execute. This method would make the call to logger and then the call to class1.Execute.
public static ExecuteAndLog(this class1 obj)
{
logger.Write(obj.ToString());
obj.Execute();
}
And then you'd simply call obj.ExecuteAndLog();
You'll need to declare an EventHandler for Class1
public event EventHandler OnExecute;
and in your execute method:
public void Execute()
{
//...
if (OnExecute != null)
OnExecute(this, null);
}
And then when you use class1 elsewhere, that's where you put your event;
private Class1 class1 = new Class1();
class1.OnExecute += SomeMethodName;
public void SomeMethodName(sender obj, EventArgs e)
{
logger.Write(class1.ToString());
}
We can make custom EventHandlers for if you want more information there, but for barebones parameterless events this should work.