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.
Related
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();
I'm trying to create an event-based sound triggering system for Unity (5.x) built on top of a project where existing code should be modified as little as possible.
The existing project has all kinds of events with different delegate signatures,
like so:
public delegate void CustomDelegateType1();
public event CustomDelegateType1 OnCustomEvent1;
public delegate void CustomDelegateType2(CustomClass param);
public event CustomDelegateType2 OnCustomEvent2;
I want to use these events to trigger sounds by adding a method to the invocation list at runtime, treating the new method just like any other subscriber. Basically, start by using reflection to get a list of events on the current GameObject, display those events in an editor dropdown on my Component, and then bind a method to that event on Awake()
The problem is: how do you add a generic method to ANY event type (as long as it returns void), given the different number and types of params used in the different delegates?
[Edit: this also needs to run on mobile devices, and hence Reflection.Emit usage isn't viable]
It seems that you only want to register to certain events (since you want to choose in the UI.
Why don't you juste create a classe that manages your audio and that registers to all the events you need.
Then you could create different methods that matches all the needed delegates.
I don't really see why you would need to add the method only at runtime since you plan to do it on Awake, it means you plan to know all the needed methods anyway
I was able to adapt the code presented in this SO answer to solve my problem:
public static Delegate AddHandler(this object obj, string eventName, Action action)
{
// Filter list by event name
EventInfo ev = obj.GetType().GetEvents().Where(x => x.Name == eventName).FirstOrDefault();
if (ev == null)
{
Debug.LogWarning(eventName + " not found on " + obj.ToString());
return null;
}
// Simple case - the signature matches so just add the new handler to the list
if (ev.EventHandlerType == typeof(Action))
{
ev.AddEventHandler(obj, action);
return action;
}
Delegate del = CreateDelegate(ev, action);
ev.AddEventHandler(obj, del);
return del;
}
public static void RemoveHandler(this object obj, string eventName, Action action)
{
// Filter list by event name
var ev = obj.GetType().GetEvents().Where(x => x.Name == eventName).FirstOrDefault();
if (ev == null)
{
Debug.LogWarning(eventName + " not found on " + obj.ToString());
return;
}
// Simple case - the signature matches so just add the new handler to the list
if (ev.EventHandlerType == typeof(Action))
{
ev.RemoveEventHandler(obj, action);
}
else
{
Delegate del = CreateDelegate(ev, action);
ev.RemoveEventHandler(obj, del);
}
}
private static Delegate CreateDelegate(EventInfo ev, Action action)
{
// Retrieve the parameter types of the event handler
var parameters = ev.EventHandlerType.GetMethod("Invoke").GetParameters();
ParameterExpression[] parameters2 = Array.ConvertAll(parameters, x => Expression.Parameter(x.ParameterType, x.Name));
MethodCallExpression call;
// We are "opening" the delegate and directly using the Target and the Method.
if (action.Target == null) // Event is static
call = Expression.Call(action.Method);
else // Event is instanced
call = Expression.Call(Expression.Constant(action.Target), action.Method);
var exp = Expression.Lambda(ev.EventHandlerType, call, parameters2);
return exp.Compile();
}
Unfortunately I couldn't optimize this method to be efficient enough for production usage, as it causes a massive hit on init() execution time, so I ended up writing a bunch of audio helper classes instead.
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?
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);
I would like to remoe duplicate delegates from an event. So I have written the following code. It works fine. My application is a time critcal application. Is there any other optimized mechansim to achieve the same. Please help me
public void FireEvent()
{
Dictionary<Delegate, Delegate> dic = new Dictionary<Delegate, Delegate>();
Delegate[] m = this.Paused.GetInvocationList();
foreach (Delegate d in m)
{
Delegate dout;
if (dic.TryGetValue(d, out dout))
{
continue;
}
else
{
dic.Add(d, d);
}
d.DynamicInvoke(new object[2] { this, null });
}
}
Problem with original approach
If this is really a time critical application, I would strongly advise changing your code.
You construct and populate a new Dictionary<Delegate, Delegate> on every method call. This is quite wasteful.
You use DynamicInvoke, which has slower performance than regular invocation to begin with.
You construct a new object[] to pass as a parameter to your DynamicInvoke call, again on every FireEvent call.
This is a bit of a corruption of the established mechanism for event handling.
Suggestion for improved approach
Here's a much better solution, in my opinion: instead of having this FireEvent method which bends over backwards to ignore duplicate delegates that have been added, why not just prevent delegates from being attached to the event multiple times in the first place?
private HashSet<EventHandler> _pausedHandlers = new HashSet<EventHandler>();
public event EventHandler Paused
{
add // will not add duplicates
{ _pausedHandlers.Add(value); }
remove
{ _pausedHandlers.Remove(value); }
}
Then you can simply raise the event in the much more conventional, time-tested way, confident that no delegates have been attached to the event more than once.
protected void OnPaused()
{
foreach (EventHandler handler in _pausedHandlers)
{
try
{
handler(this, EventArgs.Empty);
}
catch
{
// up to you what to do here
}
}
}
Note on the concept of "duplicate delegates"
The comments to this answer have shed some light on the issue of delegate equality that I felt it would be beneficial to include in this answer. If you're interested, take a look at the following code example I wrote in an attempt to make this topic a little bit easier to understand.
class Program
{
static void Main(string[] args)
{
// Even though the code for FirstHandler and SecondHandler is the same,
// they will not (nor should they) be considered equal for the purpose
// of detecting duplicates.
EventHandler handler1 = FirstHandler;
EventHandler handler2 = SecondHandler;
// Since handler2 and handler3 point to the same method, on the other
// hand, they will (and ought to) be treated as equal.
EventHandler handler3 = SecondHandler;
// So this prints 'False'...
Console.WriteLine(handler1.Equals(handler2));
// ...and this prints 'True'.
Console.WriteLine(handler2.Equals(handler3));
// Now take a look at the code for SetAnonymousHandler below. The
// method accepts an EventHandler by reference and compares it for
// equality to a delegate pointing to an anoymous lambda method. If the
// two are equal, it sets the EventHandler to this new delegate and
// returns true; otherwise it returns false.
// This prints 'True', as handler1 is not equal to the delegate
// declared within the SetAnonymousHandler method.
Console.WriteLine(SetAnonymousHandler(ref handler1));
// HOWEVER, now it prints 'False' for a simple reason: the delegate
// declared in SetAnonymousHandler points to an actual method. The fact
// that it is an anonymous method simply means that the compiler
// automatically generates a "proper" method for it in IL (to see what
// I mean, read the comment at the bottom of this class). So now,
// as with handler2 and handler3 above, handler1 and the delegate
// declared in SetAnonymousHandler point to the same method and are
// therefore equal; the method returns false.
Console.WriteLine(SetAnonymousHandler(ref handler1));
Console.ReadLine();
}
static void FirstHandler(object sender, EventArgs e)
{
Console.WriteLine("Testing");
}
static void SecondHandler(object sender, EventArgs e)
{
Console.WriteLine("Testing");
}
static bool SetAnonymousHandler(ref EventHandler handler)
{
EventHandler anonymous = (sender, e) => Console.WriteLine("Testing");
if (!handler.Equals(anonymous))
{
handler = anonymous;
return true;
}
else
{
return false;
}
}
// Note: When the above method is compiled, the C# compiler generates code
// that would look something like this if translated back from IL to C#
// (not exactly, but the point is just to illustrate that an anoymous
// method, after compilation, is really just like a "proper"
// -- i.e., non-anonymous -- method).
//static bool SetAnonymousHandler(ref EventHandler handler)
//{
// EventHandler anonymous = SetAnonymousHandler_Anonymous;
// if (handler.Equals(anonymous))
// {
// handler = anonymous;
// return true;
// }
// else
// {
// return false;
// }
//}
//static void SetAnonymousHandler_Anonymous(object sender, EventArgs e)
//{
// Console.WriteLine("Testing");
//}
}