Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

When should I use delegates?

Discussion in 'Scripting' started by surajsirohi1008, Jan 15, 2018.

  1. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Like this delegate:

    Code (CSharp):
    1.  
    2.         System.Action<int> print = i => Debug.Log (i);
    3.  
    4.         print (2);
    5.  
    The same thing is simply achieved by writing this:

    Code (CSharp):
    1. void print(int i){
    2.  
    3. Debug.Log(i);
    4.  
    5. }
    6.  
    7. print(2);
    then why use delegates in unity programming?
     
  2. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    One example, which I like to use, is as arguments to async methods.

    Code (CSharp):
    1. void DoSomethingAsync(Action<bool> onDone)
    2. {
    3.     ...
    4. }
    5.  
    6. void Example()
    7. {
    8.     DoSomethingAsync((result)=>{
    9.         if(result) Debug.Log("Success");
    10.         else Debug.Log("Failed");
    11.     });
    12. }
     
    TaleOf4Gamers likes this.
  3. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Could you explain your code? I'm a beginner.
     
  4. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    I'm giving the DoSomethingAsync method something to call once it's done (because it takes an unknown amount of time).

    Here's a more concrete example.

    Code (CSharp):
    1. // LoadUrl takes a string as argument 1 and a method with bool and string as argument 2. The passed in method is called once the WWW operation is finished.
    2. IEnumerator LoadUrl(string url,Action<bool,string> onDone)
    3. {
    4.     var www = new WWW(url);
    5.  
    6.     yield return www;//wait until operation finished
    7.  
    8.    // Call delegate onDone with arguments.
    9.     if(!string.IsNullOrEmpty(www.error)) onDone(false,null);
    10.     else onDone(true,www.text);
    11. }
    12.  
    13. // This method can be passed into second argument of LoadUrl because its signature matches delegate (bool,string)
    14. void MyCallback(bool success,string text)
    15. {
    16.         if(result) Debug.Log("Success: "+text);
    17.         else Debug.Log("Failed");
    18. }
    19. void Example()
    20. {
    21.     // MyCallback is called by LoadUrl.
    22.     StartCoroutine(LoadUrl("http://somesite.com",MyCallback));
    23. }
     
  5. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I'd also say, typically with a call back, for example after some asynchronous processing. Then you still have the choice between a call back to an interface or a delegate.

    I also use them in an expression parser, so I can for example directly assign Mathf.Cos as the implementation of "cos".

    That's really the advantage of a delegate over an interface. The name of the function to call is not determined by any rule, so you have the opportunity to reuse an existing function.
     
  6. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Thanks for the explanation of your code. One last thing, why couldn't we have done this without using delegates?
     
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Wall'o'text coming...

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
    It allows you to reference methods/functions.

    There's a few parts to it.

    First there's the delegate type:
    Code (csharp):
    1. public delegate void Action(int input);
    It's made up of a few parts:
    Accessor modifier - (public,private,internal) works like access modifier of any type

    delegate declaration - informs the compiler we're defining a delegate, similar to how with a class you'd say 'public class ....'

    return type - (void) the type the delegate would return when invoked like a method, in this case void meaning it doesn't return a type

    name - (Action) name the type, just like you'd name a class,struct,enum, etc.

    parameters - (int input) what inputs does the method take, you can have multiple.

    Note - there are multiple built in delegate types already predefined. Like the System.Action<T> generic delegate type you used in your example. These built in's save you from having to define your own delegate types.

    Next you have the delegate instance:
    Code (csharp):
    1. System.Action<int> f = new System.Action<int>(SomeMethod);
    2.  
    3. public void SomeMethod(int input)
    4. {
    5.      Debug.Log(input);
    6. }
    7.  
    Note, C# allows you to implicitly instantiate delegates though:
    Code (csharp):
    1.  
    2. System.Action<int> f = SomeMethod; //the compiler interprets this the same as the former with new System.Action<int>(SomeMethod)
    3.  
    4. public void SomeMethod(int input)
    5. {
    6.      Debug.Log(input);
    7. }
    8.  
    Just like with classes, you have to instantiate delegates. When you do you pass in the method you'd like to reference.

    In this case f now references SomeMethod.

    This means you can ambiguously pass around a reference to SomeMethod to anywhere, or store it for later. The ability to have a reference to things can be very powerful (and is one of the key features of OOP, things having object identity... object identity is the ability to reference something uniquely).

    So, lets say we had 2 classes, one creates the other, and then wants to hand it a method to callback on, on some event.

    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     void Start()
    6.     {
    7.             this.gameObject.GetComponent<FooB>().RegisterCallback(this.OnCallback);
    8.     }
    9.  
    10.     private void OnCallback()
    11.     {
    12.         Debug.Log("FooB had its trigger entered.");
    13.     }
    14.  
    15. }
    16.  
    17. public class FooB : MonoBehaviour
    18. {
    19.  
    20.     private System.Action _callback;
    21.  
    22.  
    23.     public void RegisterCallback(System.Action callback)
    24.     {
    25.         _callback += callback;
    26.     }
    27.  
    28.     private void OnTriggerEnter(Collider other)
    29.     {
    30.         if(_callback != null) _callback();
    31.     }
    32.  
    33. }
    34.  
    Now FooB can reference the callback method without the need for referencing FooA completely.

    Note, this is actually what the C# 'event' is all about. It is a protected delegate in that other objects can add references to the event/delegate, but only the owner can invoke it.

    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     void Start()
    6.     {
    7.             this.gameObject.GetComponent<FooB>().callback += this.OnCallback;
    8.     }
    9.  
    10.     private void OnCallback()
    11.     {
    12.         Debug.Log("FooB had its trigger entered.");
    13.     }
    14.  
    15. }
    16.  
    17. public class FooB : MonoBehaviour
    18. {
    19.  
    20.     public event System.Action callback;
    21.  
    22.     private void OnTriggerEnter(Collider other)
    23.     {
    24.         if(_callback != null) _callback();
    25.     }
    26.  
    27. }
    28.  
    Delegates can be used in other ways as well. Such as andymads brought up for asyncs, or you can with coroutines. Lets get a coroutine example.

    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     void Start()
    6.     {
    7.             this.gameObject.GetComponent<FooB>().DoStuff(this.AfterFooBDoesStuff);
    8.     }
    9.  
    10.     private void AfterFooBDoesStuff()
    11.     {
    12.         Debug.Log("FooB finished stuff.");
    13.     }
    14.  
    15. }
    16.  
    17. public class FooB : MonoBehaviour
    18. {
    19.  
    20.     public void DoStuff(System.Action callbackOnFinished)
    21.     {
    22.         this.StartCoroutine(this.DoStuffRoutine(callbackOnFinished));
    23.     }
    24.  
    25.     private IEnumerator DoStuffRoutine(System.Action callbackOnFinished)
    26.     {
    27.         //do stuff
    28.         //wait some time
    29.         yield return new WaitForSeconds(5f);
    30.         if(callbackOnFinished != null) callbackOnFinished();
    31.     }
    32.  
    33. }
    34.  
    ...

    You can do this with out delegates.

    For example Java accomplishes this with interfaces:

    (this is not written in Java, but in C#, using Java techniques)
    Code (csharp):
    1.  
    2. public interface ICallback
    3. {
    4.     void Invoke();
    5. }
    6.  
    7. public class FooA : MonoBehaviour
    8. {
    9.  
    10.     void Start()
    11.     {
    12.             this.gameObject.GetComponent<FooB>().DoStuff(new Callback(this));
    13.     }
    14.  
    15.     private void AfterFooBDoesStuff()
    16.     {
    17.         Debug.Log("FooB finished stuff.");
    18.     }
    19.  
    20.  
    21.     private class Callback : ICallback
    22.     {
    23.         private FooA _owner;
    24.         public Callback(FooA owner)
    25.         {
    26.             _owner = owner;
    27.         }
    28.      
    29.         public void Invoke()
    30.         {
    31.             _owner.AfterFooBDoesStuff();
    32.         }
    33.     }
    34.  
    35. }
    36.  
    37. public class FooB : MonoBehaviour
    38. {
    39.  
    40.     public void DoStuff(ICallback callbackOnFinished)
    41.     {
    42.         this.StartCoroutine(this.DoStuffRoutine(callbackOnFinished));
    43.     }
    44.  
    45.     private IEnumerator DoStuffRoutine(ICallback callbackOnFinished)
    46.     {
    47.         //do stuff
    48.         //wait some time
    49.         yield return new WaitForSeconds(5f);
    50.         if(callbackOnFinished != null) callbackOnFinished.Invoke();
    51.     }
    52.  
    53. }
    54.  
    Note, this has a WAY LOT MORE syntax to it. So yeah... C# just compressed it down into its own data type delegate.

    (yes there are a lot of different ways the Java style could have been done to reduce the syntax overhead. I wanted to go with an example that sort of mirrors how delegates work. For example you could have just had FooA implement ICallback on its own, but now FooA is always a ICallback. Or you could have had the Debug.Log inside the Callback class, but then you defined said method independent of FooA.... this example above is how you get an object relation similar to that of a delegate)

    Furthermore, C# builds a collection of delegates right into delegate itself. So you can chain multiple delegates into a single object with little work compared to the Java example. Hence the += syntax from way before.

    Lastly, anonymous delegates:
    Code (csharp):
    1. System.Action f = () => Debug.Log("Hellow World");
    OR
    Code (csharp):
    1. System.Action f = delegate() {
    2.     Debug.Log("Hello World");
    3. }
    These are called anonymous methods.

    It allows you to define a function/method inline in your code, instead of it having to be a member of another type (class/struct).

    This allows for even more freedoms on top of the already available features of delegates.

    See:
    https://docs.microsoft.com/en-us/do...ments-expressions-operators/anonymous-methods

    Under the hood it effectively works like my Java-like example (well that depends on the shape of the anonymous method, but I'm not going to get into all the nitty gritty details of it).

    And in the end facilitates MUCH shorter syntax to get the same effective outcome.

    The anonymous methods again get object identity and can do a lot of weird things. Such as access variables scoped to the method inwhich you've inlined.

    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     void Start()
    6.     {
    7.     }
    8.  
    9.     private void OnTriggerEnter(Collider other)
    10.     {
    11.         this.gameObject.GetComponent<FooB>().DoStuff(() => {
    12.             Debug.Log("FooB Completed doing stuff, now lets destroy other");
    13.             Object.Destroy(other);
    14.         });
    15.     }
    16.  
    17.  
    18. }
    19.  
    20. public class FooB : MonoBehaviour
    21. {
    22.  
    23.     public void DoStuff(System.Action callbackOnFinished)
    24.     {
    25.         this.StartCoroutine(this.DoStuffRoutine(callbackOnFinished));
    26.     }
    27.  
    28.     private IEnumerator DoStuffRoutine(System.Action callbackOnFinished)
    29.     {
    30.         //do stuff
    31.         //wait some time
    32.         yield return new WaitForSeconds(5f);
    33.         if(callbackOnFinished != null) callbackOnFinished();
    34.     }
    35.  
    36. }
    37.  
    Note how in this example the anonymous method reference's the Collider 'other'.

    You couldn't do this if the anonymous method wasn't inlined. Because FooB expects a System.Action, not a System.Action<Collider>. Sure you might say "well change FooB to accept System.Action<Collider> and pass along the Collider to it"... yeah, but that fundamentally changes FooB. FooB isn't always going to be called by a FooA, and it otherwise does give two poops about that Collider. It just does stuff.... maybe that's just an animation call or something, what if that animation is called for various reasons... not just entering colliders.

    You could have done it like this:
    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     private Collider _other;
    6.  
    7.     void Start()
    8.     {
    9.     }
    10.  
    11.     private void OnTriggerEnter(Collider other)
    12.     {
    13.         _other = other;
    14.         this.gameObject.GetComponent<FooB>().DoStuff(this.OnFooBFinished);
    15.     }
    16.  
    17.     private void OnFooBFinished()
    18.     {
    19.         Debug.Log("FooB Completed doing stuff, now lets destroy other");
    20.         Object.Destroy(_other);
    21.         _object = null;
    22.     }
    23.  
    24. }
    25.  
    But what if OnTriggerEnter happens again in the duration it takes FooB to finish... well then either you have to not react to the OnTriggerEnter, or your '_other' gets overwritten.

    To effectively get the same effect as the anonymous method you'd have to do this:
    Code (csharp):
    1.  
    2. public class FooA : MonoBehaviour
    3. {
    4.  
    5.     void Start()
    6.     {
    7.     }
    8.  
    9.     private void OnTriggerEnter(Collider other)
    10.     {
    11.         var token = new Callback(other);
    12.         this.gameObject.GetComponent<FooB>().DoStuff(token.Invoke);
    13.     }
    14.  
    15.     private class Callback
    16.     {
    17.      
    18.         private Collider _other;
    19.      
    20.         public Callback(Collider other)
    21.         {
    22.             _other = other;
    23.         }
    24.      
    25.         public void Inovke()
    26.         {
    27.             Debug.Log("FooB Completed doing stuff, now lets destroy other");
    28.             Object.Destroy(_other);
    29.         }
    30.      
    31.     }
    32.  
    33. }
    34.  
    Or in the Java-like:
    Code (csharp):
    1.  
    2. public interface ICallback
    3. {
    4.     void Invoke();
    5. }
    6.  
    7. public class FooA : MonoBehaviour
    8. {
    9.  
    10.     void Start()
    11.     {
    12.     }
    13.  
    14.     private void OnTriggerEnter(Collider other)
    15.     {
    16.         this.gameObject.GetComponent<FooB>().DoStuff(new Callback(other));
    17.     }
    18.  
    19.     private class Callback : ICallback
    20.     {
    21.      
    22.         private Collider _other;
    23.      
    24.         public Callback(Collider other)
    25.         {
    26.             _other = other;
    27.         }
    28.      
    29.         public void Inovke()
    30.         {
    31.             Debug.Log("FooB Completed doing stuff, now lets destroy other");
    32.             Object.Destroy(_other);
    33.         }
    34.      
    35.     }
    36.  
    37. }
    38.  
    And actually... under the hood this is precisely what the anonymous method is doing. But instead compacting it into a much shorter syntax.
     
    joerad and surajsirohi1008 like this.
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Sorry... I'm the resident types way too much guy.

    Before becoming a programmer, I went to college to be a math teacher... so it carries over.
     
    Kiwasi likes this.
  9. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    yey! :D
     
  10. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    This is probably somewhere in LoD's long reply, but the entire and only point is that [print] from your example, is a _variable_. You can have [print=Debug.Log;] but then later have [print=displayInTextBox;]. In your program, print(n) will call whichever function you pointed it to.

    So your Q is sort of like asking "why would anyone use n=2; print(n); when they could just print 2?"

    The stuff about interfaces or events or OOP are sort of red herrings. Even the word delegate is confusing (C# uses the word for a few different things.) Yes, function pointers can be used for events, or sometimes replaced with inheritance tricks, but they are a different thing and can be used all by themselves. My favorite explanation/examples are in taxesforcatses-dot-com, the C#/Unity programming notes, in the chapter on function pointers.
     
    surajsirohi1008 likes this.
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    A red herring is something intended to mislead or distract.

    My bringing up of interfaces was a direct answer to the OP's question:
    An answer to a question is not misleading.

    My bringing up of an event is just an example of the use of delegates.

    And OOP? What? The 'type' delegate is specifically a structure with which to give a function pointer object identity. It explicitly has to do with OOP, since it implements the principal of object identity.

    I'd agree it can be confusing, but it doesn't really use it in a lot of different places. It uses it in 2... the declaration of the type, and the instance of the type. There's a lot of this in programming... I have a class, and an instance of that class. I have a struct, and a value of that struct. All the syntactical uses of the keyword 'delegate' is to facilitate the construction of either a delegate type, or a delegate instance/object.

    Yes, they can be used for.... because delegate has a use. If it didn't, why would it exist? The inclusion of such examples were because again the OP asked what the use of delegates are, not just what they are. So examples were included... like events, and callbacks.

    Do you mean this website?
    http://taxesforcatses.com/

    Why didn't you just link to it? Which article on that site?

    Wait... isn't that your avatar on that page... wait, whose the registrar of that site?

    Did you really just name drop your own site in the 3rd person?
     
  12. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Which topic covers that example? (Topics on your website)
     
  13. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    The common name for variables which can hold functions (what C# calls delegates) is "function pointer." So that's the name of the chapter about them on the taxesforcatses site. It's all the way on the bottom of the programming page, in the advanced topics. It assumes you're using Unity and know C#, but not any advanced Unity knowledge.

    And yes, that's a picture of Julia on the site, the same one I use here. It was also my faculty picture, way back. I don't like to slap my name on stuff, but figured the same picture was good enough so anyone who wanted to know would know. I'm sorry you(LordOfDuct) took offense at Red Herring. In common use it's not a pejorative term - you're not accusing someone of trying to be tricky (wikipedia's not always great, but that's even the last sentence of the first para.)
     
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I didn't find it offensive.

    I found it inaccurate.

    @surajsirohi1008 - here's the link to the thing Owen-Reynolds is referring to. I don't know why he insists on vaguely pointing in the general direction of it:
    http://taxesforcatses.com/bookW/ch28FstClssFunc.pdf

    I also don't see how his multi-page list of examples of uses of delegates that are his favorite, aren't red-herrings as well.

    Then again, I'm of the opinion they're using the word incorrectly.
     
  15. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    One classic example (and the first or one of the first I was made aware of) is for sorting. The function can be generalized to a point, but the specifics on how to sort would take a delegate.

    I first came across using them in qsort and bsearch (in c programming) ~ Not super relevant ;)
     
  16. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    Because you might prefer the HTML version. And the extra two clicks from the top of the site provide context, which I think is important for web-browsing. You can see it's part of a textbook, which might mean you skip it - lots of people just hate textbooks. And I also redo the page occasionally and break links.
     
  17. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    See... I didn't even know there was an html version. Your website isn't exactly easy to find your way around.

    I had to first off find your website (since you failed to link to that, not sure if you think it's against the rules to post links here or something)
    http://taxesforcatses.com/

    You said it was all the way at the bottom of the 'programming page'... I saw a 'Intro To Programming', so I guessed that was what you meant. So I went here:
    http://taxesforcatses.com/TOC.shtml

    Though all the way at the bottom was a Revision History. Above that something about Comments. But above that there was this section:


    Which I did see something about 'Function Pointers' at... which was only a pdf link.

    OHHHH, I see at the top of that page there is a 'Part I & II as HTML' that goes here:
    http://taxesforcatses.com/bookW/TOC2.shtml

    And I guess near the bottom was this:


    Not sure which of those 3 links I'm supposed to use... but I guess I should have to earn my privilege to read your novel.

    But hey, OP, there you go. The HTML version of it if you want it as well.
     
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Although I do like that you call them function pointers, although Microsoft specifically explains why they're actually different from C function pointers here:
    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/using-delegates

    That I find to be a relatively decent explanation of what a delegate is.

    And is why I linked to it in my original post.

    ...

    Sorry OP, I may have derailed a bit. I'll shut up now in regards to all that.
     
    Last edited: Jan 15, 2018
  19. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    Sure it is. It works the same way in C#. You write the same 2-input compare function returning -1/0/+1 for less/same/more, then pass it to the built-in sort. For example: W.Sort(lenCompare); where lenCompare could be a simple: int lenCompare(string a, string b) { if(a.len<b.len) return -1; else return +1; }

    It's a little confusing because Sort says it takes a "Comparer," but that's just a typedef for the same old: int(T,T) signature. You never need to use that word. It also has an overload using the similarly-name IComparer. That's a superclass it you want to use the Delegate Design Pattern.

    BUT, the OP's question seems to be about adding a variable. Since we can call W.Sort(lenCompare);, why would we need to use System<string,string,int> compFunc; compFunc=lenCompare; W.Sort(compFunc);? Of course, you'd use it for the same reason you use any other variable - in case you want it to vary.
     
  20. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Right.. Yes. I think that's what I meant. :)

    nb. the part about not really relevant, was where I first used/learned it. Not that it was used there. =)
     
  21. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I use delegates for state machines and program flow. I don't need to but they helped me declutter a lot of code and in my book that is why I should be using something. To make something easier to work with. Performance difference was not noticeable.
     
  22. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Thanks for the link bro.
     
  23. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    Yeah, I also got kinda lost on the website too but thanks for the direct link to the article.
     
  24. surajsirohi1008

    surajsirohi1008

    Joined:
    Jun 21, 2016
    Posts:
    266
    I read the chapter 35 till "Passing functions into functions", it's very well written and beginner friendly. The "special weapon" and " count numbers in an array" examples were really helpful to understand the use of delegates. Thanks for the help!