instantiate and initalialize the delegate - c#

I have a quick question about coding delegates.
Why do you have to
pf = t.Print; //instantiate and initialize the delegate.
code
delegate void PrintFunction();
public class Test
{
public void Print()
{
Console.WriteLine("Print 1 --instance");
}
public static void Print1()
{
Console.WriteLine("Print 2 -- static");
}
}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
PrintFunction pf;
pf = t.Print; //instantiate and intialize the delegate
pf += Test.Print1;
pf += t.Print;
if (null != pf)
{
pf();
}
else
Console.WriteLine("delegate is empty");
}
}
}

Delegates are immutable reference types, their default value is null. The default constructor for the delegate accepts a method that matches its signature.
So you can do this:
var pf = new PrintFunction(Test.Print1);
pf += t.Print;
Or:
var pf = Test.Print1;
pf += t.Print;
Or:
var pf = null;
pf += Test.Print1;
pf += t.Print;
Edit:
Source that Delegates are reference types: MSDN
A reference type contains a pointer to another memory location that
holds the data. Reference types include the following:
String.
All arrays, even if their elements are value types
Class types, such as Form
Delegates

You do not have to call the code pf = t.Print;. It is perfectly acceptable to write pf = null;.
If you remove the line pf = t.Print; you'll just discover that the compiler is giving you the error "CS0165 Use of unassigned local variable 'pf'".
This is no different than if you wrote the code:
bool flag;
if (flag)
{
}
It's nothing to do with it being a delegate. It's just a compiler error.

so you have to initialize the delegate the first before you call it, like when calling a class. Also the delegate signature has to match the method that is calling the delegate.
Thanks everyone for the help.
Should of coped it before i posted the question.
Thanks again.

By default delegate value is null you should initialize it to something and then use it or add other methods. In your sample there will be 2 times called t.Print and one time Test.Print1.
Good practice is to initialuize delegate to nullable object and then use it without any null checking like below.
PrintFunction pf = () => {};
pf += Print1;
pf += Print;
pf += Print1;
pf();

Related

Can't get the first method name in a multicast delegate

I have this code:
public void AddMenuRow(FuncInvoker i_FuncToAdd) // add a row to menu.
{
if (d_Lines == null)
{
d_Lines = new FuncInvoker(i_FuncToAdd);
}
else
{
d_Lines += i_FuncToAdd;
}
}
for adding methods to the invoke list.
And now I want to print the name of each method to the console, so I made this:
public void Show()
{
int count = 1;
string name = null;
Console.WriteLine(m_Title);
foreach (FuncInvoker list in d_Lines.GetInvocationList())
{
name = list.Method.Name;
Console.WriteLine((count++) + ". " + name);
}
}
The problem is in the first method name, which always prints "invoke" for some reason.
The next methods in the delegate link work fine.
Can someone help me with this? I have tried everything.
In this line:
d_Lines = new FuncInvoker(i_FuncToAdd);
...you're actually creating a new delegate instance that wraps the original delegate. The method-target of this new delegate will be the Invoke method of the original delegate (assuming it's unicast), which explains the behaviour you're observing.
The obvious workaround is to not use a wrapper and just copy a reference to the original delegate to the variable:
d_Lines = i_FuncToAdd;
But you might as well do away with your 'special-case' branch completely and just do (assuming the argument can't be null):
public void AddMenuRow(FuncInvoker i_FuncToAdd)
{
d_Lines += i_FuncToAdd;
}
This will work fine since Delegate.Combine (which is what the += syntax becomes) is speced to return a reference to the second delegate if the first delegate is null, rather than throwing an exception.

How to manually invoke an event?

I have the following line in C#:
_timer.ElapsedTick += _somefunction1;
_timer.ElapsedTick += _somefunction2;
_timer.ElapsedTick += _somefunction3;
How to invoke all methods subscribed to _timer.ElapsedTick without specifying the _somefunction ? Somewhere along this pseudo-line
invoke(_timer.ElapsedTick);
You can't invoke an event which is owned by another type. An event can only be invoked from the inside of the class which declares it.
Can it be done using conventional C#? No (as previously stated). But using reflection it is possible. Here is some tested code based on the answer to this MSDN forum thread:
class InvokeFromMe
{
public event EventHandler RaiseMe;
}
class Program
{
static void Main(string[] args)
{
var fromMe = new InvokeFromMe();
fromMe.RaiseMe += fromMe_RaiseMe;
fromMe.RaiseMe += fromMe_RaiseMe1;
fromMe.RaiseMe += fromMe_RaiseMe2;
FireEvent(fromMe, "RaiseMe", null, EventArgs.Empty);
}
static void fromMe_RaiseMe(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 0 Raised");
}
static void fromMe_RaiseMe1(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 1 Raised");
}
static void fromMe_RaiseMe2(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 2 Raised");
}
public static void FireEvent(object onMe, string invokeMe, params object[] eventParams)
{
MulticastDelegate eventDelagate =
(MulticastDelegate)onMe.GetType().GetField(invokeMe,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, eventParams);
}
}
}
UPDATE
I'm not familiar with the System.Timer.Timer class, so I'm not sure what is different from my provided example. You could perhaps try something like:
public static void FirePublicEvent(object onMe, string invokeMe, params object[] eventParams)
{
MulticastDelegate eventDelagate =
(MulticastDelegate)onMe.GetType().GetField(invokeMe,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public).GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, eventParams);
}
}
well i end up here about searching how to simply start event, which is not having any custom args or anything. I know OP question is different! but i had like to share, i hope it helps someone.
Unsimplify version:
var Handler = EventNameHere;
if (Handler != null)
{
Handler(this, EventArgs.Empty);
}
Simplify version:
EventNameHere?.Invoke(this, EventArgs.Empty);
You could create a function that runs them all:
public void RunAllFuncs()
{
_somefunction1();
_somefunction2();
_somefunction3();
}
This is a work around, you can't loop through the Elapsed event. But to invoke them all, let the timer do the work. This code for System.Timer, not sure which timer you are using.
Presuming the timer is already enabled:
int interval = timer.Interval;
ElapsedEventHandler handler = null;
handler = (s,e) =>
{
timer.Interval = interval; // put interval back to original value
timer.Elapsed -= handler;
};
timer.Elapsed += handler;
timer.Interval = 1; // 1 millisecond, pretty much going to fire right now (as soon as you let it)
Something like that will fire the events, but your original interval will be restarted. You may have to do some math in there if you want to keep the original tick pattern.
I think you probably need to use InvokeMember:
public void GetMethod(string methodName){
var args = new Object[] { [INSERT ARGS IF NECESSARY--SET TO NULL OTHERWISE] };
try
{
var t = new [INSERT NAME OF CLASS THAT CONTAINS YOUR METHOD]();
Type typeInfo = t.GetType();
var result = typeInfo.InvokeMember(methodName, BindingFlags.InvokeMethod, null, t, args);
return result;
}
catch (Exception ex)
{
return ex;
}
}
Then call it like this:
_timer.ElapsedTick += GetMethod("[INSERT METHOD NAME AS STRING]");
Be sure to include this:
using System.Reflection;
Good Luck!
Based on M.Babcock's answer above, with some slight reflection method updates.
Replaced GetField with GetTypeInfo and GetDeclaredField, and dlg.Method with dlg.GetMethodInfo.
using System.Reflection;
...
public static void FireEvent(this object onMe, string invokeMe, params object[] eventParams)
{
TypeInfo typeInfo = onMe.GetType().GetTypeInfo();
FieldInfo fieldInfo = typeInfo.GetDeclaredField(invokeMe);
MulticastDelegate eventDelagate = (MulticastDelegate)fieldInfo.GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.GetMethodInfo().Invoke(dlg.Target, eventParams);
}
}
With .NET 6 (may work with other versions, too), the following generic method may be helpful. Like in other answers it uses reflection to invoke the EventHandler:
private void InvokeEvent<T, TEvent>(T target, string eventName, TEvent #event)
{
// some reflection magic to fire an event from outside the target class:
if(target == null)
{
throw new ArgumentNullException(nameof(target));
}
Type targetType = target.GetType();
FieldInfo? fi = targetType.GetField(eventName, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
if(fi != null)
{
EventHandler<TEvent>? eventHandler = fi.GetValue(target) as EventHandler<TEvent>;
if(eventHandler != null)
{
eventHandler.Invoke(this, #event);
}
}
}
It can then be invoked like this:
const string eventName = nameof(Algorithm.Received);
DomainEvent #event = new DomainEvent(payload);
InvokeEvent(targetObject, eventName, #event);
Notes:
The above code also works if multiple methods were added with += to the event handler.
If you want use this code in product you may want to consider more error handling such as parameter checking or throwing an exception if a specific event handler wasn't found.
With each major version of .NET the code using reflection may need to be changed. As .NET 6 is cross-platform running on Windows, Linux, MacOS, etc. I suspect the likelihood of changes breaking the above code is reduced, although not zero.
We are using this code in our automated test suite. In case the code using reflection does in fact change, we only need to change this one method.
Building on the answers before me, the following worked for me:
EventNameHere(null, EventArgs.Empty);
Depending on the signature of the ElapsedTick event, you can fire the event by calling it like a method from inside the class in which it is declared. For example:
ElapsedTick(this, new EventArgs());
You can only call events from inside the class that declares it. If you wrote the timer class, you would invoke it like this:
this.ElapsedTick(this, eventArgs);
Outside the timer class you may do as follows to get similar behavior:
delegate void delegateSample(object sender, EventArgs e);
delegateSample d;
d += new delegateSample(_somefunction1);
d += new delegateSample(_somefunction2);
d(this, EventArgs.Empty);
delegateSample should actually be the type of the ElapsedTick event, instead of declaring another delegate type. I just didn't know the name of that delegate. Just make sure you add each function the ElapsedTick event and the delegate variable you declare then you can call them all from the delegate outside the Timer class.

Can a method be attached to a delegate with predefined parameters?

Sometimes I encounter cases where I have to attach a method to a delegate but the signature doesn't match, like trying to attach abc down there to somedelegate with the string parameter being "hi".
public class test
{
//...
public void abc(int i, string x)
{
//Do Something
}
//...
}
public class test2
{
somedelegate x;
//...
public test2()
{
//Do Something
test y = new test();
x += y.abc(,"hi");
}
delegate void somedelegate(int i);
}
I can work it around by creating another delegate with the correct signature then attaching it but it seems so unnecessarily complex. Can you do something like this in C#? Thanks.
EDIT: I guess there closest to what I wanted to achieve is:
x += (int i) => abc(i, "hi");
Yes, you can do this with closures
[there's a nice treatment of the subject on msdn, but like anything else in there it's hard to find]
The Big Picture
Write a method that can take all the parameters you need
Inside that method you return an anonymous method with the delegate-target signature it requires
This method's call is itself the parameter in the delegate instantiation
Yes, this is a bit Matrix-y. But way cool.
delegate void somedelegate (int i);
protected somedelegate DelegateSignatureAdapter ( string b, bool yesOrNo, ...) {
// the parameters are local to this method, so we'll go w/ that.
// our target delegate requires a single int parameter and void return
return (int a) => {
// your custom code here
// all calling arguements are in scope - use them as needed
}; // don't forget the semicolon!
}
// our delegate call
somedelegate myWarpedDelegate = new somedelegate (DelegateSignatureAdapter("someString", true));
myWarpedDelegate (2543);
myWarpedDelegate(15);
Just Googling for '.net delegate optional parameters' returns some results that may be useful:
Can a Delegate have an optional parameter?
VB.NET - Is there a way to utilize optional parameters in delegates? (Or Plans to Allow this?)
Optional Parameters and Delegates
Update (researching this some more, and helped by the first link above):
Could you perhaps use the Invoke method, which accepts any delegate?

C# Delegates and Events logic and syntax issues

As my code suggests, I'm trying to create a delegate which will point to the StringBuff method BuffString, which creates a StringBuilder that is going to have a fair amount of settings, etc.
My problem is that, for some reason, no matter what it is I try I can't pass the reference to the StringBuff class I made within my Sprite class to the delegate's constructor without receiving an error. Ontop of that, I feel like creating an event may be useful to help initiate the delegate.
The main problem is that I'm just now barely grasping these two concepts, as well as how to use them as replacements for function pointers which are allowed in other programming languages.
If anyone has any idea on what it is I need to do to make this work, I would definitely appreciate it.
Here's the code:
public class StringBuff
{
private static StringBuilder stringBuffer = new StringBuilder();
public static StringBuilder BuffString(string _string) //--may possibly have to use IntPtr to reference stringBuffer here.
//This is the equivalent to the "strbuff_new" C++ method variant, designed to update the stringBuffer.
{
int iCounter = 0;
stringBuffer.Append(_string + " ");
iCounter += _string.Length + 1;
if (iCounter == stringBuffer.Capacity - 1)
{
stringBuffer.Capacity += stringBuffer.Capacity;
}
return stringBuffer;
}
}
public delegate void UpdateStringBuffer(StringBuff sender);
public class Sprite : SpriteInterface.ISprite
{
private StringBuff stringBuff = new StringBuff();
public event UpdateStringBuffer stringBuffEvent
{
add
{
Console.WriteLine("Adding");
stringBuffEvent += value;
}
remove
{
Console.WriteLine("Removing...");
stringBuffEvent -= value;
}
}
static void Main()
{
new Sprite().stringBuffEvent += new UpdateStringBuffer(stringBuff);
}
}
I believe you are in need for some reading. Refer to the following:
Events Tutorial
Introduction to Delegates and Events
Events and Delegates simplified
You are misunderstanding the use of events and delegate.
When you want to add an Event Handler to an event, you pass a delegate of the same type as the event (which you did correctly)
But when you create a delegate, what you should pass in the constructor (most of the time) is a Method Name and not some variable, since a delegate is a kind of pointer to a (list of) functions.
I reccomend you to read more about delegates as Akram Shahda suggested but just for now i'll tell you that the method that you should pass as parameter to the delegate constructor should have the same signature - means return the same value and accept the same parameters. so for example you could have:
// This method have the same signature as UpdateStringBufferDelegate
public void SomeMethod (StringBuff buff)
{
// Doing somthing here
}
And then you can do in your main:
// Passing method's name and not a variable!!
new Sprite().stringBuffEvent += new UpdateStringBuffer(SomeMethod);
The Actuall parameters that will be passed to the function itself (some StringBuff) only determined at the time of the invokation of the event.
You should read more about that.
Good Luck!
you are doing it wrong,
new Sprite().stringBuffEvent += new UpdateStringBuffer(stringBuff);
Above code is invalid due to following reasons.
1. stringBuff that your UpdateStringBuffer is taking is an instance of StringBuff within Sprite.
2. You are accessing stringBuff from the static Main method which does not have any idea about stringBuff where it is located.
1- The delegate's constructor can only have a parameter Method. Ex
public delegate void UpdateStringBuffer(StringBuff sender);
2- You can declare ur event and add a method to define ur method in ur Splite class. Ex:
public event UpdateStringBuffer stringBuffEvent;
public ProcessUpdateStringBuffer(UpdateStringBuffer yourMethod)
{
stringBuffEvent += yourMethod
}
3- and from ur main u can define ur method to the event and invoke it like this:
Sprite sprite = new Sprite();
sprite.ProcessUpdateStringBuffer(UpdateStringBuffer(urMethod));
sprite.stringBuffEvent(ur parameters);

Subscribing to events

When subscribing to events in .NET do I need to create a new instance of the delegate such as
toolbarControl1.OnUploadEventHandler +=
new ToolbarControl.UploadEventHandler(toolbarControl1_OnUpload);
Or is it better to do the following?
toolbarControl1.OnUploadEventHandler += toolbarControl1_OnUpload;
Thanks in advance.
The second one takes less coding and both mean the same.
You might want to consider using generic version of EventHandler - EventHandler<TEventArgs>. You will save on declaring all those delegates.
You can also use lambda expressions, ie.
toolbarControl1.OnUploadEventHandler +=
(sender, args) => { /* Your code goes here */ };
You can do both, so its your preference.
You can also do things like this for very simple handlers to save writing a delegate function:
toolbarControl1.OnUploadEventHandler += (s, e) => uploadCount +=1;
The code speaks for itself:
using System;
class Program
{
void SomeMethod(object sender, EventArgs e) { }
event EventHandler SomeEvent;
static void Main(string[] args)
{
var prog = new Program();
// Demonstrate that they are equivalent
prog.SomeEvent += new EventHandler(prog.SomeMethod);
prog.SomeEvent -= prog.SomeMethod; // Sugar for new EventHandler(prog.SomeMethod)
Console.WriteLine("Number of SomeEvent subscribers is {0}", (prog.SomeEvent != null ? prog.SomeEvent.GetInvocationList() : new Delegate[0]).Length);
// Why are they equivalent?
var d1 = new EventHandler(prog.SomeMethod);
var d2 = new EventHandler(prog.SomeMethod);
Console.WriteLine("Delegates are reference equal {0}", Object.ReferenceEquals(d1, d2));
Console.WriteLine("Delegates are equivalent {0}", d1 == d2);
}
}
They are equivalent. The second one only provides better readability.
They are equivalent unless you need to keep a reference to the delegate so as to unsubscribe.

Categories