C# - Why is there an error using EventHandler<T>? - c#

Doing a Pluralsight video and can't find out why it's wrong.
Error Message on this point:
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Error Message:
Cannot implicity convert to type
'System.EventHandler<AB_Events.WorkPerformedEventArgs>' to
'AB_Events.WorkPerformedHandler'
Snippets
public delegate int WorkPerformedHandler(object sender,WorkPerformedEventArgs e);
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
protected virtual void OnWorkPerformed(int hours, WorkType workType)
{
var del = WorkPerformed as WorkPerformedHandler;
if (del != null)
{
del(this, new WorkPerformedEventArgs(hours, workType));
}
}
}
And
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Console.ReadKey();
}
public static void worker_WorkPerformed(object sender, WorkPerformedEventArgs e)
{
throw new NotImplementedException();
}
}

Although methods and anonymous functions are implicitly convertible to a delegate type that matches their signature, a delegate instance is not implicitly convertible to another delegate type.
You need to use either EventHandler<TEventArgs> or your WorkPerformedHandler, but not both:
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
//...
}
Or:
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
//...
}
Also, because worker_WorkPerformed can be implicitly converted to either type, the most concise syntax would be this:
worker.WorkPerformed += worker_WorkPerformed; // Implicit conversion
If you do go with your WorkPerformedHandler delegate, make sure you change the return type to void as per the signature of worker_WorkPerformed:
public delegate void WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
It is generally not advisable to return from an event handler anyway, as there can be multiple subscribers.

what i have done is to replace WorkPerformedHandler by EventHandler<WorkPerformedEventArgs>
public delegate int WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
protected virtual void OnWorkPerformed(WorkPerformedEventArgs e)
{
WorkPerformed?.Invoke(this, e);
}
}
event subscription :
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);

Related

In my C# class dll I want to call a method with fixed name "func_X", that should be placed at the C# app where the dll is called. How can I do that?

Hello I have packed my standard code in a class dll. I am calling this dll from my C# service apps. But in the dll,at one point, there should be called a method with fixed name "func_X" that is not standard and has to be defined by the dll caller app. How can I realise this?
The challanging point is that the func_X is not called at a fix point in my dll. According to the flow, it is called at a different point.
My service where I call the dll
using Concheetah_Class; // my dll
namespace Concheetah_Service_Bahmuller
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Concheetah_Class.Main_Prog main_Prog = new Concheetah_Class.Main_Prog();
main_Prog.Main_Start(); // starting point of my dll
}
public void func_X()
{
// some supplementary code
}
}
}
My dll code
public void Main_Start()
{
// some long code
func_X(); // Here I should call the method that has to be defined on the caller side
// some long code
}
Update-1 My dll code
System.Timers.Timer timer1 = new System.Timers.Timer();
public void Main_Start()
{
Initialize_timer1();
}
public void Initialize_timer1()
{
timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent_timer1);
timer1 = 35;
timer1.Start();
}
private void OnTimedEvent_timer1(object sender, EventArgs e)
{
//some code
func_x();
}
You will to need pass the function to your dll program.
Update according to your latest edit:
Approach 1: You can pass your function to this constructor of Main_Prog and store it in a variable.
public class Main_Prog
{
System.Timers.Timer timer1 = new System.Timers.Timer();
Action func_x;
public Main_Prog(Action func_x)
{
this.func_x = func_x;
Initialize_timer1();
}
public void Initialize_timer1()
{
timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent_timer1);
timer1.Interval = 35;
timer1.Start();
}
private void OnTimedEvent_timer1(object sender, EventArgs e)
{
this.func_x();
}
}
Approach 2: Instead of storing it globally pass the function to OnTimedEvent:
public class Main_Prog
{
System.Timers.Timer timer1 = new System.Timers.Timer();
public Main_Prog(Action func_x)
{
Initialize_timer1(func_x);
}
public void Initialize_timer1(Action func_x)
{
timer1.Elapsed += (sender, args) => OnTimedEvent_timer1(sender, args, func_x);
timer1.Interval = 35;
timer1.Start();
}
private void OnTimedEvent_timer1(object sender, EventArgs e, Action func_x)
{
func_x();
}
}
In your Service1 pass func_x as an argument.
protected override void OnStart(string[] args)
{
Concheetah_Class.Main_Prog main_Prog = new Concheetah_Class.Main_Prog();
main_Prog.Main_Start(func_X);
}
In your Main_Prog receive it as an Action.
public void Main_Start(Action func_X)
{
func_X();
}
Depending on your need you can switch between Func & Action.
Action is used when the return type is void and Func is used when return type is not void.
I am not sure if I understand your question correctly but if I can take a stab at it, from what I am understanding you would like to create a method in your caller code than needs to be invoked by the code in your the dll that has already been build?
If so, I would use delegates to achieve this. You can add a parameter to your Main_Start method that accepts either a Action (void method) or Func (method with return type)
Example:
public class ActionExample // Delegate with NO return type
{
public void Run()
{
Main_Start(PrintName);
}
public void PrintName(string name)
{
Console.WriteLine($"My name is: {name}");
}
// Code in your packaged dll
// the string value in the generics represents the input value of the method
public void Main_Start(Action<string> methodToRun)
{
methodToRun("John Doe");
}
}
public class FuncExample // Delegate WITH return type
{
public void Run()
{
Main_Start(GetHelloMessage);
}
public string GetHelloMessage(string name)
{
return $"My name is: {name}";
}
// Code in your packaged dll
// First string in the generics represents input paramater and last string represents return paramater of the method
public void Main_Start(Func<string, string> methodToRun)
{
string message = methodToRun("John Doe");
Console.WriteLine(message);
}
}

get publisher class field from sender

I want to get my field >>> NumberOfElementsInMyList from sender, How Can I do it? I couldn't find this kind of question here (what I have seen, was about windows forms), so ...
class Program
{
static void Main(string[] args)
{
Publisher PublisherObject = new Publisher();
PublisherObject.NumberAdded += PublisherObject_NumberAdded;
PublisherObject.AddNumber(int.Parse(Console.ReadLine());
}
static void PublisherObject_NumberAdded(object sender, EventArgs e)
{
//I want to write on the console "NumberOfElementsInMylist"
//I tried:
//sender.NumberOfElementsInMylist -- not works
//Publisher obj=(publisher)sender and then sender.NumberOfElementsInMylist
//not works
Console.WriteLine("number of elements in list is ---> "+ ???? );
}
}
class Publisher
{
public event EventHandler NumberAdded;
public int NumberOfElementsInMyList;
List<int> MyList=new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAdded(this, new EventArgs());
}
}
To literally answer your question, the reason why you can't access the NumberOfElementsInMyList field is because when you create the EventArgs, your instance of Publisher is being cast as an object (which you can do since all classes inherit from object.) So to see the properties (or field) of Publisher, you have to cast the sender as Publisher.
var numberOfElements = ((Publisher)sender).NumberOfElementsInMyList;
A downside to this is that hypothetically, sender might not be a Publisher. Because sender is an object, it could technically be anything.
You can also create your own event handler delegate and event args instead of using the boilerplate EventHandler delegate.
public delegate void NumberAdded(Publisher source, NumberAddedEventArgs eventArgs);
public class NumberAddedEventArgs : EventArgs
{
public NumberAddedEventArgs(int numberAdded, numberOfItemsInList)
{
NumberAdded = numberAdded;
NumberOfItemsInList = numberOfItemsInList;
}
public int NumberAdded { get; private set; }
public int NumberOfItemsInList { get; private set; }
}
public class Publisher
{
public event EventHandler NumberAddedEvent;
public int NumberOfElementsInMyList;
List<int> MyList = new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAddedEvent?.Invoke(this, new NumberAddedEventArgs(NumberToAdd,
NumberOfElementsInMyList));
}
}
var numberOfElementsInList = args.NumberOfItemsInList; // much better!
The (object sender, EventArgs args) is a strange convention. In any other scenario we would create strongly-typed methods and delegates. But in this case there's a tendency to use something that's not strongly-typed because it's a convention.
You can simply cast the sender, can't you?
var publisher = (Publisher)sender;
Console.WriteLine(publisher.NumberOfElementsInMyList);
Or more safely:
Console.WriteLine(
(sender as Publisher)?.NumberOfElementsInMyList?.ToString() ?? "sender is not a publisher!");

error learning how to use custom events

i'm learning how to use custom events in c#, but i get some errors
i get "An object reference is required for the nonstatic field, method, or property" in the bold words
so i tried following this
but case 1 couldn't be tried 'cause TypeChanged is already a nonstatic method (i think)
in case 2 i get "impossible to acces BicycleType as an instance reference, qualify it as a type"
public class Bicycle
{
public event EventHandler TypeChanged;
private string type;
...
public string BicycleType {
get { return this.type; }
set {
this.type = value;
if (this.TypeChanged != null)
this.TypeChanged( this, new EventArgs() );
}
}
public Bicycle() {}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("heila!");
Bicycle istanza = new Bicycle();
istanza.TypeChanged += new EventHandler(**istanza_TypeChanged**);
istanza.BicycleType = "io";
Console.WriteLine("io");
}
void istanza_TypeChanged(object sender, EventArgs e) {
Console.WriteLine("rofkd");
}
}
the tutorial i followed told me i can use events "as" methods, maybe i'm wrong here?
the code is completely similar to te tutorial code
sorry for my bad english and thanks in advance
As you are registering the event from the main method, which is static, the event handler (istanza_TypeChanged) has to be made static too.
You problem is that Main is static and can therefore not access nonstatic members of the class Program. However you try to access istanza_TypeChanged. That is what is causing your exception.
You have to make istanza_TypeChanged static too to solve the issue
class Program
{
static void Main(string[] args)
{
Console.WriteLine("heila!");
Bicycle istanza = new Bicycle();
istanza.TypeChanged += new EventHandler(**istanza_TypeChanged**);
istanza.BicycleType = "io";
Console.WriteLine("io");
}
static void istanza_TypeChanged(object sender, EventArgs e)
{
Console.WriteLine("rofkd");
}
}
Register the event from a non-static context or change your event to be static.
Change istanza_TypeChanged to the following:
private static void istanza_TypeChanged(object sender, EventArgs e)
{
Console.WriteLine("rofkd");
}
The following fired the event for me:
public class Bicycle
{
public event EventHandler TypeChanged;
private string type;
public string BicycleType
{
get { return this.type; }
set
{
this.type = value;
if (this.TypeChanged != null)
this.TypeChanged(this, new EventArgs());
}
}
public Bicycle()
{
}
private class Program
{
private static void Main(string[] args)
{
Console.WriteLine("heila!");
Bicycle istanza = new Bicycle();
istanza.TypeChanged += istanza_TypeChanged;
istanza.BicycleType = "io";
Console.WriteLine("io");
}
private static void istanza_TypeChanged(object sender, EventArgs e)
{
Console.WriteLine("rofkd");
}
}
}

Omit sender of delegates

Is it possible to send reference of 'sender' without specifying it explicitly as a parameter in delegate-based event handling?
I have a internal class which raises some events and I want to call the events explicitly for test purposes.
public class Manager {
public class DataStruct {
public int Id { get; private set; }
public event EventHandler Event1; // Can't be called by other classes
public void fireEvent1(Event1();} // So another caller...
// Delegates *can* be called by other classes
public delegate void DelegateHandler(DataStruct sender);
public DelegateHandler NewEvent;
public void DelegateHandler(DataStruct sender) {
MessageBox.Show(string.Format(
"{0} raises event", sender.Id));
}
}
}
// Form1 ///////////////////////////////////////////////////////////////////
partial class Form1 {
Manager.DataStruct dsRaiser, dsListener;
private void Form1_Load(object sender, EventArgs e) {
dsRaiser.Event1 += dsListener.SOME_HANDLER;
dsRaiser.NewEvent += dsListener.DelegateHandler;
}
private void button1_Click(object sender, ...) {
dsRaiser.fireEvent1(); // No argument needed but fireEvent1, not Event1().
}
private void button2_Click(object sender, ...) {
dsRaiser.NewEvent(dsRaiser); // Way to omit parameter dsRaiser?
}
//////////////////////////////////////////////////////////////////////////
If your handler method needs to use the sender's reference, then you HAVE to pass that reference.
If not, just declare a void parameterless delegate, like Action.
But when thinking of events, that parameter should be passed by the class that raises the event itself. (Remember events are not meant to be called from outside).
So, if you really want to use a simple delegate instead of an event, you will have to pass the parameters.
If you need the sender, you will need to do exactly what you did with the event: create a method to "raise" the delegate, and in that method you pass this as the sender.
But considering you have to do exactly the same thing in both cases, I'd surely use the event.
public class DataStruct {
public int Id { get; private set; }
public event EventHandler Event1; // Can't be called by other classes
// you need to pass those parameters to the event when called.
public void fireEvent1{Event1(this, new EventArgs());}
// Delegates *can* be called by other classes, but only with all parameters passed.
public delegate void DelegateHandler(DataStruct sender);
public DelegateHandler NewEvent;
// To avoid passing parameters, you need to do exactly what you did with the event
public void RaiseDelegate() { NewEvent(this); }
public void DelegateHandler(DataStruct sender) {
MessageBox.Show(string.Format(
"{0} raises event", sender.Id));
}
}
Yes, it is possible.
Just store the sender inside the subscription when subscribing to an event.
If we had a delegate declared like this:
public Action NewEvent; // No need to be DelegateHandler
Then we can use c# compiler to generate such a subscription for us using anonymous delegates:
dsRaiser.NewEvent += () =>
{
dsListener.DelegateHandler(dsRaiser);
};
Anything, which we reference inside our anonymous handler is automatically captured for us (both dbListener and dsRaiser in this case).
Or, if we want explicit declaration of the subscription:
class MySubscription
{
private readonly DataStruct _raiser;
private readonly DataStruct _listener;
public MySubscription(DataStruct raiser, DataStruct listener)
{
_raiser = raiser;
_listener = listener;
}
public void HandleTheSubscription()
{
_listener.DelegateHandler(_raiser);
}
}
And this is how we subscribe:
private void Form1_Load(object sender, EventArgs e) {
var mySubscription = new MySubscription(dsRaiser,dsListener);
dsRaiser.NewEvent += mySubscription.HandleTheSubscription;
}
As you can see MySubscription is defined by us and we can store any objects there.

Raise event through a general method

I'm trying to create a reusable method for a more complicated event execution. I can't get it to compile or run with framework events that don't follow the EventHandler<Type> pattern. I would like to avoid reflection if possible as it will be a heavily used event.
I've created a test console app below which illustrates the problem:
using System;
using System.Collections.Specialized;
namespace CallEventsViaMethod
{
public class TestEventArgs : EventArgs { }
class Program
{
static void Main(string[] args)
{
MyProgram program = new MyProgram();
program.Go();
Console.ReadKey(false);
}
}
public class MyProgram
{
public event EventHandler<TestEventArgs> TestEvent;
public event NotifyCollectionChangedEventHandler CollectionChangedEvent;
public void Go()
{
TestEvent += new EventHandler<TestEventArgs>(MyProgram_TestEvent);
CollectionChangedEvent += new NotifyCollectionChangedEventHandler(MyProgram_CollectionChangedEvent);
// Want a reusable method I can use to conditionally execute any event
GeneralEventExecutor.Execute<TestEventArgs>(TestEvent, new Object(), new TestEventArgs());
GeneralEventExecutor.Execute<NotifyCollectionChangedEventArgs>(TestEvent, new Object(), new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
void MyProgram_TestEvent(object arg1, TestEventArgs arg2)
{
Console.WriteLine("Custom event ran");
}
void MyProgram_CollectionChangedEvent(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("NotifyCollectionChangedEventHandler event ran");
}
}
public static class GeneralEventExecutor
{
public static void Execute<T>(EventHandler<T> eventToRaise, object sender, T eventArgs) where T : EventArgs
{
if (eventToRaise == null)
return;
Delegate[] registeredEventHandlers = eventToRaise.GetInvocationList();
foreach (EventHandler<T> eventHandler in registeredEventHandlers)
{
object target = eventHandler.Target; // Need access to the Target property
// * Code deciding if should invoke the event handler *
eventHandler.Invoke(sender, eventArgs);
}
}
}
}
Error messages are:
error CS1502: The best overloaded method match for
'CallEventsViaMethod.GeneralEventExecutor.Execute(System.EventHandler,
object,
System.Collections.Specialized.NotifyCollectionChangedEventArgs)' has
some invalid arguments
error CS1503: Argument 1: cannot convert from
'System.EventHandler' to
'System.EventHandler'
I understand why I'm getting the error, but can't figure out a way round it.
Replace your your generic Execute<T> as below
public static void Execute<T>(Delegate eventToRaise, object sender, T eventArgs) where T:EventArgs
{
if (eventToRaise == null)
return;
Delegate[] registeredEventHandlers = eventToRaise.GetInvocationList();
foreach (Delegate eventHandler in registeredEventHandlers)
{
object target = eventHandler.Target; // Need access to the Target property for conditions
// * Code deciding if should invoke the event handler *
eventHandler.DynamicInvoke(sender, eventArgs);
}
}

Categories