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. Dismiss Notice

How often do you use delegates and interfaces in your games?

Discussion in 'Scripting' started by ptgodz, Dec 8, 2016.

  1. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    I've been using unity for about 4 months now and I'm loving every second of it. I come from no coding background whatsoever but have picked up C# reasonably well up to now. However, I've yet to delve into interfaces and delegates. I have downloaded free templates on the asset store and seen developers write some pretty impressive games without ever using them.

    My question is not whether I should bother to learn how to use them as I plan to delve into them this week (Also if anyone has any good resources, I'd appreciate it as I have tried before to understand these properly but struggled to a degree)

    My question is how much did understanding these concepts and implementing them make a difference to what you were able to produce?

    On a side note. I've recently started looking into json since watching a gamergrind video, This is new to me as I would typically put information directly into a script as opposed to using a text docmuent. My question regarding this is, As I progress I'd like to experiment with storing data on servers and wondered whether json was a sensible way to go as opposed to using something like XML(I've never used either in a project up to now)

    Thanks for any answers
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    OK... you seem to be talking about 3 different topics at the same time.

    Delegates - object identity for functions

    Interfaces - contract definitions for the interface of an object (in this case interface is different from Interface... where interface is the public members of a type, and Interface is the contract... as in interfaces like you used it)

    Serialization - representing data as text with either json or xml




    So...

    Delegates - yes, get used to them, they're VERY useful. Sure you could get around using them, but you'll probably end up using them indirectly. Especially with things like 'UnityEvent', if you use those.

    Interfaces - not as important, but I use them a lot. Do you have any specific questions about them?

    Json vs XML - you had one specific question... which is better. I find both of them useful, xml especially for its expressiveness. But json has the benefits of being more compact, less verbose, and supporting a more object oriented approach to its structure.

    It's really up to you which to use... I often go json or binary.

    For server storage, you may want to consider a database instead. And use json for transferring your data from client to server.

    Again... if you have more specific questions, ask away. But keep in mind having 3 distinct topics in one thread can make for some confusing conversation.
     
  3. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    Thanks for the response lordofduct.

    Yeah I probably shouldn't have bundled it all into one question. Could you recommend any tutorials that you found particularly helpful? I often find that the code I've wrote, while doing the job I want it to do, just doesn't seem as efficient as it could be and often put it down to the fact that I'm not using delegates or interfaces

    I've watched a couple of tutorials on interfaces, and have to admit that I have struggled with them in regards to what benefit they would actually bring. If I'm right from what I've understood, they basically allow you to organise a class? Judging by what I've seen through tutorials, and I am probably completely wrong, I can't help but feel that I am just essentially duplicating functions that don't actually do anything?

    I think I will probably look into json to begin with then as it seems probably better for a beginner to work with.
     
  4. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Absolutely use Interfaces and delegates; they're essential for decoupling. They both allow you to interact with classes without being aware of the class itself. It's not so much a programming efficiency thing (in fact, use of either is slower than a direct call) as well, a user efficiency thing (using them can dramatically decrease refactoring, and other benefits).

    I tend to serialize as XML if I want something human-readable for testing, then switch my serialization to binary for final. Binary serialization is far faster, and will produce much smaller files.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    Tutorials... no, I don't use tutorials usually. Especially not for these concepts as I learned them many years.

    I have noticed people struggle with interfaces, I myself had a bit of a battle with them myself when I was learning OOP concepts.

    So ok... I'll try to explain them in the way I understood them... which means I'm going to have to talk about something NOT to do with interfaces as you may see them in C#.

    WARNING - HUGE POST AHEAD

    OK... so OOP (object oriented paradigm) operates on some fundamental concepts:

    Encapsulation, Abstraction, Polymorphism

    and Inheritance, though the OOP community at large has mostly realized inheritance isn't exactly perfect, and shouldn't get the fundamental label so much anymore. Which honestly is where interfaces as you see them in C# sort of spawns from.

    OK, so what are these things.

    Encapsulation
    This is the concept that objects should have 2 distinct parts. An interior and an exterior. These are called its 'implementation' and its 'interface' respectively.

    I like to use the VCR as an example (I don't know how old you are, but hopefully you remember what the VCR/VHS is). A VCR has a chassity to it with some inputs and outputs. There is the slot in which you slide the VHS tape, buttons for play/pause/rewind/fastforward/stop/eject, and some outputs like video and audio out.

    This is its interface. It's the controlled structure through which you're allowed to interact with the VCR.

    If you crack it open though, you'll see all the gizmos and parts that make up the VCR. What actually occurs when you insert a tape, what actually occurs when you push play, so on and so forth.

    This is its implementation. It's the guts of the object that you as a user don't give 2 S***s about.

    All you need to know is the intent of those buttons and input and outputs. We don't care that play moves a lever that opens the flap of the tape and draws it around the read head of the VCR and then starts a motor spinning the tape. WE JUST CARE THAT IT MAKES VIDEO COME OUT!

    Furthermore, this interface means that Sony/JVC/etc can all make their own VCR and change around the guts to make it cheaper, or higher fidelity, or whatever they decide... yet the interface looks the same to you the end user. Same play button, same slot to stick a VHS tape in, etc.

    This is encapsulation.

    We see this in classes with the access modifier. You create 'public' and 'private' members (fields/methods/properties/etc), the public members are those outward facing buttons that can be tinkered with. You could see them like this:

    Code (csharp):
    1.  
    2. public class VCR
    3. {
    4.  
    5.     public AVSignal Output { get; };
    6.  
    7.     public void Load(VHS tape);
    8.     public void Play();
    9.     public void Pause();
    10.     public void ToggleFFW();
    11.     public void ToggleRW();
    12.     public void Stop();
    13.     public void Eject();
    14.  
    15.     public void Record(AVSignal input);
    16.  
    17. }
    18.  
    I don't show the code inside those functions/methods, because we don't care about them. This is the interface of the class (not interface as you see it in C#, we'll get to that... you screw a screw with a screwdriver).

    Abstraction
    So encapsulation has separated implementation from interface of a class.

    Abstraction is the idea of separating concrete class from the idea of how you interact with the class... its interface.

    So back to the VCR... for years disc like systems attempted to reliquish the VCR from its position as home media player. There was laser disc, and VCD, and so on... and of course the DVD which we all know and love from the late 90's on until now where even BluRay has a hard time subverting it.

    Now... there's something you may notice about all of these things though. THEY HAVE THE SAME INTERFACE.

    They all have a slot in which you load the media, they have play/pause/stop methods. Sure the DVD also comes with a 'chapter forward' and 'chapter backward' button... but they still maintaint he fastforward and rewind functionality of the VCR.

    They have COMMON interfaces. And with purpose... it makes it easy for a consumer buying this new technology to understand how to use it. You get a DVD player, and it works similar to a VCR.

    So you might see as a class:

    Code (csharp):
    1.  
    2. public class DVDPlayer
    3. {
    4.  
    5.     public AVSignal Output { get; };
    6.  
    7.     public void Load(DVD disc);
    8.     public void Play();
    9.     public void Pause();
    10.     public void ToggleFFW();
    11.     public void ToggleRW();
    12.     public void Stop();
    13.     public void Eject();
    14.  
    15.     public void ChapterForward();
    16.     public void ChapterBackward();
    17.  
    18. }
    19.  
    Now you might notice, we no longer have a Record functionality, because DVD's can't be recorded to. But we've gained the ChapterFW and Back functions.

    Furthermore the Load function accepts a different input.

    But other than that the interface is VERY similar.

    What if we could describe these things as related?

    So now we get into the contention of inheritance... I'm going to show you this example with inheritance/abstract class, but now a days this is considered poor design. But keep in mind OOP has been around a long time and has grown... but yeah, this is why I removed inheritance from the fundamental concepts of OOP.

    Code (csharp):
    1.  
    2. public class VideoMedia
    3. {
    4.  
    5. }
    6.  
    7. public class DVD : VideoMedia
    8. {
    9.  
    10. }
    11.  
    12. public class VHS : VideoMedia
    13. {
    14.  
    15. }
    16.  
    17. public class VideoMediaPlayer
    18. {
    19.  
    20.     public AVSignal Output { get; };
    21.  
    22.     public void Load(VideoMedia media);
    23.     public abstract void Play();
    24.     public abstract void Pause();
    25.     public abstract void ToggleFFW();
    26.     public abstract void ToggleRW();
    27.     public abstract void Stop();
    28.     public abstract void Eject();
    29.  
    30. }
    31.  
    32. public class VCR : VideoMediaPlayer
    33. {
    34.  
    35.     public AVSignal Output { get; };
    36.  
    37.     public overloads void Load(VHS tape) : base.Load(tape);
    38.     public void Play();
    39.     public void Pause();
    40.     public void ToggleFFW();
    41.     public void ToggleRW();
    42.     public void Stop();
    43.     public void Eject();
    44.  
    45.     public void Record(AVSignal input);
    46.  
    47. }
    48.  
    49. public class DVDPlayer : VideoMediaPlayer
    50. {
    51.  
    52.     public AVSignal Output { get; };
    53.  
    54.     public overloads void Load(DVD disc) : base.Load(disc);
    55.     public overrides void Play();
    56.     public overrides void Pause();
    57.     public overrides void ToggleFFW();
    58.     public overrides void ToggleRW();
    59.     public overrides void Stop();
    60.     public overrides void Eject();
    61.  
    62.     public void ChapterForward();
    63.     public void ChapterBackward();
    64.  
    65. }
    66.  
    So now, we could be handed a VideoMediaPlayer, told it's media is already loaded, and call Play/Pause/Stop/etc to our hearts content... never needing to concern ourselves with what sort of videomedia it plays. Which leads to....

    Polymorphism

    Polymorphism is the concept of how you can treat 2 objects similarly because their interfaces are similar.

    It's the direct result of abstraction.

    Inheritance

    So the big evil monster that is inheritance.

    Inheritance comes with weird issues that may not be recognizable in the examples I posted above. But it all comes down to that 'implementation' part.

    See 'VideoMediaPlayer' can contain implementation in it for whatever purpose. Maybe it sets up the AVSignal stuff, because that's most the same from media player to media player... but wait, what if you do that, but then a NEW media video output standard comes about (HDMI?), this means we must retrofit it into the existing VideoMediaPlayer class... and what if that standard doesn't mesh well with outdated mediums like VHS and LaserDisc, which are inherently analogue signals.

    Another issue comes up is that what if you define a class that several classes inherit from. But you create one that is both a VideoMediaPlayer but ALSO an AudioMediaPlayer (like a CD-player, or mp3-player).

    Code (csharp):
    1.  
    2. public class AudioMediaPlayer
    3. {
    4.     AudioSignal Output {get;}
    5.  
    6.     public void Play();
    7.     public void Pause();
    8.     public void Stop();
    9.     public void Skip();
    10. }
    11.  
    12. public class CDPlayer : AudioMediaPlayer
    13. {
    14.  
    15. }
    16.  
    17. public class MP3Player : AudioMediaPlayer
    18. {
    19.  
    20. }
    21.  
    22. public AdvancedDVDPlayer : VideoMediaPlayer, CDPlayer, MP3Player
    23. {
    24.     //so on so forth
    25. }
    26.  
    If I call Play, what am I playing?

    If MP3Player already has code implementing Play, and so does CD player... how do I tell the compiler which function I actually want called?

    Everything is getting weird now...

    This is beginning to border on what is called the 'diamond problem' in inheritance:
    https://en.wikipedia.org/wiki/Multiple_inheritance

    Multiple inheritance is proving to create quagmires... so what do we do? Ban multiple inheritance (which C# does), but how do we get to be a DVD/CD/MP3 player?

    In steps a NEW concept that was introduced to OOP to replace Inheritance...

    Composition
    Composition is the idea that instead of inheriting from a type, you instead consume that part and make it accessible through the interface.

    Code (csharp):
    1.  
    2. public class CDPlayer : AudioMediaPlayer
    3. {
    4.  
    5. }
    6.  
    7. public class MP3Player : AudioMediaPlayer
    8. {
    9.  
    10. }
    11.  
    12. public class DVDPlayer : VideoMediaPlayer
    13. {
    14.  
    15. }
    16.  
    17. public AdvancedDVDPlayer
    18. {
    19.     public CDPlayer CD {get;}
    20.     public MP3Player MP3 {get;}
    21.     public DVDPlayer DVD {get;}
    22. }
    23.  
    BUT, now we have a layer between us and each device. We can't just hand around the AdvancedDVDPlayer... what if we want to flip functionality based solely on what type we want it to be at that time instead of having to reach inside of it like this (breaking encapsulation/abstraction, fundamental principals of OOP).

    ...

    An explicitly defined Interface!

    Interfaces - we screw a screw with a screwdriver

    OK, so up to this point I've said the word 'interface' multiple times, but kept bringing up that it's not the interface you see in C#.

    That interface is the keyword used for explicitly defining interfaces.

    At first you might think its weird they have the same name... but that's why I say "we screw a screw with a screwdriver". We often use the same word for different things and you determine which we're talking about by context.

    "we define an interface to abstract the interface of our class"

    So now we can say this:

    Code (csharp):
    1.  
    2. public interface IAudioMediaPlayer
    3. {
    4.     AudioSignal Output {get;}
    5.  
    6.     void Play();
    7.     void Pause();
    8.     void Stop();
    9.     void Skip();
    10. }
    11.  
    12. public interface ICDPlayer : IAudioMediaPlayer
    13. {
    14.  
    15. }
    16.  
    17. public interface IMP3Player : IAudioMediaPlayer
    18. {
    19.  
    20. }
    21.  
    22. public interface IVideoMediaPlayer
    23. {
    24.  
    25.     AVSignal Output { get; };
    26.  
    27.     void Load(VideoMedia media);
    28.     abstract void Play();
    29.     void Pause();
    30.     void ToggleFFW();
    31.     void ToggleRW();
    32.     void Stop();
    33.     void Eject();
    34.  
    35. }
    36.  
    37. public interface IDVDPlayer : IVideoMediaPlayer
    38. {
    39.  
    40. }
    41.  
    Note, we don't define public/private... it's an interface. Interfaces are implicitly public, it's the public facing members of an object.

    Code (csharp):
    1.  
    2. public class CDPlayer : ICDPlayer
    3. {
    4.     public AudioSignal Output {get;}
    5.  
    6.     public void Play();
    7.     public void Pause();
    8.     public void Stop();
    9.     public void Skip();
    10. }
    11.  
    12. public class MP3Player : IMP3Player
    13. {
    14.     public AudioSignal Output {get;}
    15.  
    16.     public void Play();
    17.     public void Pause();
    18.     public void Stop();
    19.     public void Skip();
    20. }
    21.  
    22. public class DVDPlayer : IDVDPlayer
    23. {
    24.  
    25.     public AVSignal Output { get; };
    26.  
    27.     public overloads void Load(DVD disc) : base.Load(disc);
    28.     public overrides void Play();
    29.     public overrides void Pause();
    30.     public overrides void ToggleFFW();
    31.     public overrides void ToggleRW();
    32.     public overrides void Stop();
    33.     public overrides void Eject();
    34.  
    35.     public void ChapterForward();
    36.     public void ChapterBackward();
    37.  
    38. }
    39.  
    40. public AdvancedDVDPlayer : ICDPlayer, IMP3Player, IDVDPlayer
    41. {
    42.     public ICDPlayer CD {get;}
    43.     public IMP3Player MP3 {get;}
    44.     public IDVDPlayer DVD {get;}
    45.  
    46.  
    47.     public void ICDPlayer.Play() { CD.Play(); }
    48.     public void IMP3Player.Play() { MP3.Play(); }
    49.     //so on so forth
    50.  
    51. }
    52.  
    And now our AdvancedDVDPlayer can be passed around and treated as any of its 3 types, without any of the clashing that comes with multiple-inheritance.

    You MIGHT ask, dafuq I now would have to write all these forwarding methods as well!?

    Yeah... that's a sucky parts about how interfaces work in C# (and Java as well, where C# stole it from back in the late 90's). Interfaces at the time of inception were created before composition was fully embraced and was like a "best of both worlds" compromise... programming is an evolving trade... so it's a little broken when it comes to multiple composition. But for single composition, or for multiple inheritance it's great.

    Think of the collection types like 'IEnumerblae' and 'IList'. You can quickly turn any class into a collection by merely implementing these interfaces.



    Newer languages learned from this flaw in the design and come up with solutions.

    Such as 'duck typing', in languages like python due to how its interpretted and ran, you can do a thing called 'duck-typing'. The idea is that if "it walks like a duck, quacks like a duck, then it's a duck". Basically if an object has a member with some name, then it can be treated like a type that has that member name. There are flaws here again with types that have intersecting method names... but it's a way of dealing with it.

    Another... which I think is BEAUTIFUL and I hope to god that the language gets adopted far more...

    Golang

    Golang does away with inheritance all together and instead embraces composition completely.

    You don't even really have classes really... instead we remove the class and return back to 'struct' (a data structure...). But you get interfaces as well. The interfaces are implemented implicitly. And then you can composite types by just making other structs a member of your defined type:

    Code (csharp):
    1.  
    2. type shape struct {
    3.     x,y float64
    4. }
    5.  
    6. type rectangle struct {
    7.     shape shape
    8.     width,height float64
    9. }
    10.  
    rectangle can now be treated like a shape.

    It implicitly knows because you've included 'shape' as a member, that you can directly access it. You don't have to say 'myrect.shape.x' you just say 'myrect.x'.

    BUT if rectangle happened to composite a type that ALSO has an 'x' property. You can say 'myrect.shape.x' to explitly clarify which x you're talking about.

    Interfaces in this regard are also implied. If you define an interface

    Code (csharp):
    1.  
    2. type geometry interface {
    3.     area() float64
    4. }
    5.  
    Then you defined a function for rectangle called area

    Code (csharp):
    1.  
    2. func (r rectangle) are() float64 {
    3.     return r.width * r.height;
    4. }
    5.  
    Now rectangle is implicitly a geometry. And ANY function that takes in a geometry, can accept rectangles polymorphicly.

    Unfortunately golang isn't getting as popular as I wish it would. Primarily because the direction languages are leaning these days are 'functional' paradigm by and large. This is the result of software scale shrinking for the most part. We're moving towards a lot of small applications in the web that can be easily maintained in procedural and/or functional paradigms.

    And larger software in the open-source community is embracing the 'linux/gnu' philosophy of many small programs that are good at doing one thing. Which honestly I think of as moving object oriented concept out of the language, and into the OS... which I find.... maybe not the best idea. I mean think about it, these many programs are encapsulated in themselves, and often API standards are designed to make them interchangeable if you prefer another company/groups implementation. Thusly making them abstracted (the standard api) and polymorhpic (interchangeable).

    But as a result, OOP is dying out, except for in the large enterprise field where Java/C# dominate. And when efficiency is wanted (like games) C++ is king because it's baremetal, and no one wants to convert to golang there because it'd mean rewriting HUGE libraries with decades of history to them.

    ......


    Anyways... I got on a bit of a tangent with golang there.

    But yeah... THAT'S the purpose of 'interface'.

    And yes, it's VERY powerful.
     
    Last edited: Dec 9, 2016
  6. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    Got it, so BinaryFormatter is a lie :p

    On the topic of interfaces, they help you simplfy working with a set of objects with just very generic actions. see if this help you understand

    For example think about all the unique types of vehicles out there. Cars, Trucks, Semi's, Construction vehicles, tow trucks, powered lawnmowers, that barbie jeep toy. A huge majority of these vehicles have a common "interface" called a "steering wheel" if you know how to use a steering wheel in one vehicle then you likely know how to operate a steering wheel in another vehicle, even if you've never seen that vehicle before. The standard interface between the vehicle and the driver on how to steer makes adapting to a new vehicle second nature (at least in regards to steering).

    you may not know exactly how a car turns the wheels under the hood. but that doesn't matter to you cause you just mess with the steering wheel interface. it makes your job far easier when you have to work with different vehicles. if you know how to steer a Semi, you can steer a barbie jeep.

    interfaces don't make much sense in the sort term when only one object has the interface. but when you get a dozen or a hundred different objects all using the same interface, then that interface just makes the job a ton easier for the caller.
     
  7. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    Thanks for the responses guys.

    I've got a much better understanding of them now. Also thanks for taking the time to write such a in depth response lordofduct. I've read over it a couple of times to take it all in and it's made a huge difference.

    In response to Dameon, The benefits you mentioned are the main reasoning for me looking into delegates aswell. I often find I am having to change my methods multiple times when I decide add something new to my game.

    I've just finished watching a video by Programming with mosh which has helped my understanding of them. Im going to start a basic project today and implement them this week. I tend to learn a lot more by doing, things seem clearer when I put it into practice.
     
  8. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Think of interfaces as ways to add different behaviors to your objects, which has especially clear benefits when you're writing a game.

    For example, on a turn-based strategy game, I have an IRandomEvent interface that describes a variety of random things that can happen in my game each turn -- what the event is called, descriptive text, and different effects on other parts of the game. Some of those random things create events that persist for several turns, and when I write those classes I also add an IPersistentEvent interface. Others create enemies that must attack and defend and move, so on those classes I add the IEnemyOperations interface. Still others have instantaneous one-time effects and don't need any additional interfaces.

    This way, the manager that creates new random events and reports them to the player only needs to know about the IRandomEvent interface. Later on at the end of the turn, a different piece of code only needs to know about the IPersistentEvent interface. In the middle when combat is happening, that code only needs to know how to use the IEnemyOperations interface.

    Examples of other common uses are to produce different behaviors or effects for weapons, spells, projectiles, and so on. I've used interfaces to produce a group of state-machine classes that advance through and report their states with a common interface but do completely different things internally.

    In general, "behaviors" is a really good way to think about what interfaces bring to the table.
     
  9. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    Cheers MV10,

    In your example above, Am I right in thinking that I could set an event publisher in your random event manager and then use an interface which implements an event subscriber to which all classes deriving from said interface would use?
     
  10. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    Classes don't derive from an interface. A class will implement an interface. This is an important distinction, because derivation means that a type gets the implementation of its base class. Interfaces have no implementation.

    In other words, an interface is a set of method signatures (and possibly event/property accessors) that your new class promises to accept and have its own implementation for.

    On the other hand, deriving from a base class is a way to give your new class all of the implementation of that base class.

    These are different concepts.
     
    MV10 likes this.
  11. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    Sorry I did understand this and have completely mixed my terminology up. Thanks for the clarification
     
  12. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    There's one other important thing worth mentioning (probably lots but one I'm thinking of). Since interfaces are contracts and not implementations, there's no problem with implementing multiple interfaces. You can use multiple more broad interfaces to add functionality to your system, and interfaces can even implement other interfaces.

    For example:

    Code (csharp):
    1.  
    2. public interface IWeapon
    3. {
    4.     float Damage {get; set;}
    5.     float Health {get; set;}
    6.     int Weight {get; set;}
    7. }
    8.  
    9. public interface IHammer : IWeapon
    10. {
    11.     float SwingSpeed {get; set;}
    12.     void Swing();
    13. }
    14.  
    15. public interface IProjectileShooter : IWeapon
    16. {
    17.     int Capacity {get; set;}
    18.     int Range {get; set;}
    19. }
    20.  
    So the example above defines a weapon contract which all weapons will have. Different weapon types can have additional properties. Any class that implements IHammer will automatically implement IWeapon as well so SwingSpeed, Damage, Health and Weight will all need to be implmented (interfaces can define methods as well, not just properties).

    That's fine and dandy because let's say I have an inventory class with a collection of Weapons:

    Code (csharp):
    1.  
    2. public class Inventory
    3. {
    4.     public List<IWeapon> Weapons {get; set;}
    5. }
    6.  
    Since the collection is IWeapon, I could put an IHammer in it, or an IProjectileShooter in it since both of those are at their root IWeapons. In code I could check the types even:

    Code (csharp):
    1.  
    2.     var weapon = inventory.Weapons[0];
    3.    
    4.     if(weapon is IHammer)
    5.           (weapon as IHammer).Swing();
    6.  
    There are better and more efficient ways to work with your types, but this demonstrates polymorphism a bit. Additionally, you can use intefaces to augment classes with additional funcionality. For example, let's say you have a common type of effect, like taking damage. You have lots of different types of things in your game... you have Players, you have Buildings, you have Props and you have environment objects. All of those objects may (or may not) take damage. You wouldn't want them to inherit from the same base class because they're all very different. You can, however, apply an interface.

    Code (csharp):
    1.  
    2. public interface IDamageable
    3. {
    4.     void ApplyDamage(int amount);
    5. }
    6.  
    7. public class ScaryTree : Tree, IDamageable
    8. {
    9.       private int health = 100;
    10.  
    11.       public void ApplyDamage(int amount) {
    12.               health = Math.Max(health - Math.Abs(amount), 0);
    13.  
    14.               if(health == 0)
    15.                   //Player is dead
    16.       }
    17. }
    18.  
    In the example above, ScaryTree is a special kind of Tree... it inherits Tree (it could just as easily implement an ITree) that is part of what defines it's base functionality and features. But it also implements IDamageable so it can take damage.

    This also demonstrates that you can use a mix of inheritance and implementation which is often desired. Some objects will have common functionality and you don't want to code that in every class, so it can exist in the base class and you can use interfaces to define contracts for augmented features.

    And lastly, multiple interface implementations don't fall prey to the Diamond problem because they don't define any of the method or properties, just the contract for them. So if you have three interfaces with ApplyDamage(int amount) it doesn't care which one (in fact the class will resolve to any of the three).

    However, one things to beware and careful of:

    Code (csharp):
    1.  
    2. public interface IWidget
    3. {
    4.      Guid Id {get; set;}
    5. }
    6.  
    7. public interface IItem
    8. {
    9.      int Id {get; set;}
    10. }
    11.  
    In the above if you try to implement IWidget and IItem you'll have a collision because IWidget and IItem have the same property with different return types, so something to be careful of.
     
    Gozum, adriandevera and MV10 like this.
  13. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    I suppose you could (subject to kru's clarification of implement vs derive) but -- probably my use of the word "event" was unfortunate, as they don't occur arbitrarily like, say, a mouse-click event. Replace the word "event" with "occurrence" in my earlier post, maybe.

    Delegates are great for "real" event pub/sub though, and I use them heavily, too. Only one class in my game subscribes to Unity's magic "void Update" (and if I need them, FixedUpdate, LateUpdate, etc -- everything except Awake and OnDestroy). That class has a UnityUpdate delegate list to which other classes add/remove themselves. I also expose a bunch of other delegates -- for example, pointer movement and button click (because my game supports both mouse and Xbox controller interactions -- transparent to all other classes except this event manager). You do have to be very careful to ensure all subscribers remove themselves cleanly or you're likely to create hard-to-find memory leaks.

    I think you're on the right track asking about these language features.
     
    Dustin-Horne likes this.
  14. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    @MV10

    Interested in how this looks like with delegates (I have a 6502 assembler background and am slowly using more and more of the c# / oop features)

    I also use a GameManager, the only class that subscribes to start and update. It then runs the start (i call it init) and the update (I call it run) methods of all other manager classes. I just call their methods directly.
     
  15. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    I happened to post the code a few days ago:

    https://forum.unity3d.com/threads/m...ox-360-controller-ingame.156171/#post-2878539
     
  16. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Is that more efficient than left Unity call it aside from giving you more granular control?
     
  17. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    My turn-based game doesn't gain any (important) performance benefits but in a game where framerate counts, yes, you eliminate crossing the managed/unmanaged boundary. I think the idea gained widespread attention recently with that "10,000 updates" blog post from Unity late last year, but honestly I was already doing it for convenience reasons (and on one project where I needed to control the order of Update calls), so I only sort of skimmed the article.

    https://blogs.unity3d.com/2015/12/23/1k-update-calls/
     
  18. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    I'm just going to leave this here...

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. namespace MonoThreading
    6. {
    7.     public class MonoThread : MonoBehaviour
    8.     {
    9.         public static MonoThread Instance { get; private set; }
    10.  
    11.         // Previously used for subscribers, but delegate-based subscription allocates, so an Interface-based solution was used.
    12.         //        public event Action<float> ThreadHandler = (float deltaTime) => { };
    13.         private Queue<IThreadingObject> _subscribersToAdd = new Queue<IThreadingObject>();
    14.         private Queue<IThreadingObject> _subscribersToRemove = new Queue<IThreadingObject>();
    15.  
    16.         /// <summary>
    17.         /// Subscribers are called every Update, and passed a single float, which is Time.deltaTime.
    18.         /// </summary>
    19.         private List<IThreadingObject> _subscribers;
    20.         private int _subscriberCount;
    21.  
    22.         private Queue<Action> _queuedCallbacks = new Queue<Action>();
    23.         /// <summary>
    24.         /// Subscribers are called only once, at the next Update.
    25.         /// </summary>
    26.         public Queue<Action> QueuedCallbacks { get { return queuedCallbacks; } }
    27.  
    28.         private void Awake()
    29.         {
    30.             Instance = this;
    31.             _subscribers = new List<IThreadingObject>(250);
    32.         }
    33.  
    34.         private void Update()
    35.         {
    36.             while (QueuedCallbacks.Count > 0)
    37.             {
    38.                 var action = QueuedCallbacks.Dequeue();
    39.                 if (action != null)
    40.                     action.Invoke();
    41.             }
    42.             UnityEngine.Profiling.Profiler.BeginSample("Thread subscribers");
    43.             float deltaTime = Time.deltaTime;
    44.             var enumerator = _subscribers.GetEnumerator();
    45.             while (enumerator.MoveNext() != false)
    46.             {
    47.                 enumerator.Current.OnUpdate(deltaTime);
    48.             }
    49.             while (_subscribersToRemove.Count > 0)
    50.             {
    51.                 _subscribers.Remove(_subscribersToRemove.Dequeue());
    52.             }
    53.             while (_subscribersToAdd.Count > 0)
    54.             {
    55.                 _subscribers.Add(_subscribersToAdd.Dequeue());
    56.             }
    57.             UnityEngine.Profiling.Profiler.EndSample();
    58.         }
    59.  
    60.         public void Subscribe(IThreadingObject subscriber)
    61.         {
    62.             _subscribersToAdd.Enqueue(subscriber);
    63.         }
    64.  
    65.         public void Unsubscribe(IThreadingObject subscriber)
    66.         {
    67.             _subscribersToRemove.Enqueue(subscriber);
    68.         }
    69.  
    70.         private void OnDestroy()
    71.         {
    72.             queuedCallbacks = null;
    73.             Instance = null;
    74.             _subscribersToAdd = null;
    75.             _subscribersToRemove = null;
    76.         }
    77.     }
    78. }
    79.  
     
    Last edited: Dec 10, 2016
    Dustin-Horne and MV10 like this.
  19. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I need to go back and work in my pubsub system. I threw together one that's event based and pretty cool but was allocatey as it enumerated a Dictionary. But it was super handy because it let me fire a message to all subscribers and those that were using the subscription component could decide whether to handle via fixed or late update and choose how many messages to keep in the queue on a per subscriber basis. So if they received 20 messages between execute cycles and only wanted 10 it would throw the first 10 out. Or you could choose to fire the handlers immediately instead of queuing or just subscribe and handle the messages without a component at all.
     
  20. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    I also only do it out of habit, I saw that performance related blog as well, but that was not my motivation. I just find it strange not to be in full control of execution order, guess related to my assembler 6502 background. Lot's of things feel strange in Unity ;) In a good way, it's my only chance to produce something nowadays, things got way more complicated + c# is fairly easy to grasp (at least the basic stuff)

    Ps

    @MV10 - thx a lot
     
  21. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    I swear, Unity does stuff in such strange ways sometimes, and is so weird about what's exposed to the scripting system, that sometimes I feel like I'm having to work around the engine, rather than working with it. Its magic methods, and lack of control over them, is one of those times.
     
  22. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Ya learn something new every day. I thought delegates were much lower-level but it turns out they're merely (I hate the term) syntactic sugar for a new Action allocation in the IL.

    Thanks for posting that, though, Dameon, if I ever go back to that other project where update order and framerate matters a lot, I'll take a serious look at switching it to an interface approach. Any reason you didn't do something similar for the queued callback? I know Queue avoids allocs with a circular array implementation but something has to allocate the Action added to the queue.

    I don't want to stray too far off topic here, but this is the sort of thing that makes me nervous about Unity's longer-term plans to update the GC once they drag everything into a modern .NET version: during Unite they said they were unsure if they'd go with the standard MS GC or the Mono SGen GC. I just personally don't have the same level of trust for Xamarian over MS.
     
  23. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    I just never wound up switching the queued callbacks because I only actually use that part of the script in a couple of places anymore, and the allocations don't matter for those. It's essentially legacy code.
     
  24. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Digging a little more, I was very surprised to find the delegate list is a simple array that is resized one by one as each new delegate is added -- not something like an ArrayList with an initial capacity. Array resizing on every add/remove is probably an even bigger problem than the Action allocation.
     
  25. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Wow, I didn't dig far enough to find that out; that is an annoying problem. The workaround, I guess, would be to make your own ArrayList of Action delegates and bypass the C# event system entirely.
     
  26. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I'm not sure how it's handled in C++... arrays might be resizeable. In .NET though, it's necessary to allocate an entirely new array, you can't just resize it by one.

    ArrayList is bad... just use an IList<Action> or something similar.
     
  27. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    C++ arrays are static length as well.

    C++ is even more strict about allocation of memory, very hands on with the memory management.

    As Dustin-Horne stated, avoid ArrayList... it has issues with boxing and is generally the poor mans version of List<T>:
    https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx

    Also, the digging you did is probably into the Microsoft implementation of delegates.

    In mono MulticastDelegates are stored as a link list:

    Code (csharp):
    1.  
    2. // Decompiled with JetBrains decompiler
    3. // Type: System.MulticastDelegate
    4. // Assembly: mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
    5. // MVID: 9F039A96-3C23-41D4-B614-4F0E430A87D6
    6. // Assembly location: C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity\mscorlib.dll
    7.  
    8. using System.Collections;
    9. using System.Runtime.InteropServices;
    10. using System.Runtime.Serialization;
    11.  
    12. namespace System
    13. {
    14.   /// <summary>
    15.   /// Represents a multicast delegate; that is, a delegate that can have more than one element in its invocation list.
    16.   /// </summary>
    17.   /// <filterpriority>2</filterpriority>
    18.   [ComVisible(true)]
    19.   [Serializable]
    20.   public abstract class MulticastDelegate : Delegate
    21.   {
    22.     private MulticastDelegate prev;
    23.     private MulticastDelegate kpm_next;
    24.  
    25. //... so on so forth
    26.  
    All delegates inherit from this, and this is how delegate chains are implemented.

    So in the version of mono in unity (note the location of the mscorlib.dll I decompiled), you can see it's using a simple link-list pattern.

    Though, do note, there is still garbage generated when you append new delegates to a delegate chain. This is because delegates are defined to be immutable (like a string).

    I wouldn't concern myself with this that much though. Unless you're adding and removing delegates from the same event over and over in rapid succession... there really isn't a major impact. I mean heck, consider the fact that 'List' 'ArrayList' and the sort are all really just arrays underneath as well that resize as you add new entries (though they attempt to predictively resize... but this makes it use more memory than it actually needs).
     
    MV10 likes this.
  28. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yep. List<T> doubles the size of its internal array every time it resizes. It starts at 4. When you hit 3 elements it resizes to 8, then 16, etc. So as you can see it can get quite large. You can trim the list to get rid of extra empty elements and you can set the capacity. If you know (or have a rough idea) how many elements you'll have I recommend using the overload of the constructor that sets the capacity. new List<string>(500) for example.
     
  29. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    I've never actually used ArrayList...I tend to use an array, generic List, or Dictionary unless there's a specific reason to use another data structure. I just said ArrayList because somebody else had mentioned it.
     
  30. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Yeah I just had array on the brain after seeing the simple array usage (in MS), generally List is my go-to, as well. In fact, I'm not sure I've ever actually used ArrayList...
     
  31. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    I honestly find myself using HashSet<T> the most.

    I mean sure I use Arrays still for anything that needs to be configurable in the inspector. And I don't use List for this because they're usually static in size anyways.

    But outside of that I find myself using HashSet<T> the most because usually my dynamic collections don't need to be ordered. They usually are there to just remember if something was done, not if it was done before or after something else.

    Lets take for instance I have a weapon that flies through the air, and as it strikes things I store who was hit in a HashSet<T>, this way if the bullet happens to hit a second collider on the same entity, I don't mistakingly strike it twice when it should only count as once.

    Sure I could use a List for this, but HashSet's are actually more efficient at the 'Contains(x)' method, because in a List it's an O(n) operation, where as HashSet it's a O(1) operation.
     
    Kiwasi likes this.
  32. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    HashSet<T> will give you a performance benefit for larger collections. For small ones (like 5 or 10 items) the difference is negligible and may be outweighed by the extra cost of computing the hash when you insert the items into the HashSet. It also depends on what you're comparing. A collection of large strings for example will give you much better performance with a HashSet. If we're talking about objects where we're doing a reference check, it's less of a difference.
     
  33. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    Otherwise known as the difference between O(n) and O(1) operations...
     
    Kiwasi likes this.
  34. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Absolutely, but point being that using a HashSet may be an over-optimization and in some cases less performant. :) Just depends on what you're doing. If you're doing a lot of Contains type lookups, it's great. If you're frequently enumerating anyway, might as well stick with a list.
     
  35. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    It's seldom if ever less performant at Contains (the context I'm referring in). They're about equally performant at small sizes, and it out performs at large sizes.

    Also, why if you're enumerating stick with a list? The enumerating performance is very comparable if you look at their implementations (it's not like enumerating requires calculating hashes).

    Anyways, I was very clear with my use case, and it fits my use case perfectly. Especially since I often am tracking 100's if not 1000's of objects. I wouldn't call it an "over" optimization, and over optimization implies doing more work than necessary to get negligible results... this is no extra work on my part to get better performance when it's needed. Since I don't required "ordered collections" often, why use an ordered collection?
     
    Kiwasi likes this.
  36. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    For your use case but not everyone's. For hundreds or thousands of objects the enumeration performance is the same but insertion is slower on the HashSet because of the need to compute the hash. That wasy point. Evaluate each collection type and pick the best based on your needs. :)
     
  37. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Since this is an advice thread, I thought of another delegates issue I ran into once: if you add a new delegate while you're responding to a call from that same list of delegates, that new delegate is going to be called immediately.

    It's probably normally not an issue, but I had at least one edge case where it was a hassle. I had built my own GUI prefab that registered itself for mouse clicks. If the processing of that mouse click spawned a new GUI prefab, the click in that new instance would fire immediately. Working around this required a subscription arrangement very similar to what Dameon posted so that new subs happened at the start of the following Update.
     
  38. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    Are you suggesting that you, inside of a handler A for a delegate (directly or indirectly), you add a subscription to that delegate, then the newly added subscription handler B is invoked during the same invocation? That sounds suspect with raw c# delegates. Although if you rolled your own multicast delegate implementation with lists of delegates, then I could see this being an issue. Unsubscribing from a delegate during invocation is one of the reasons Resharper suggests the following pattern whenever invoking an event or delegate:
    Code (csharp):
    1. var handler = WhateverYourEventIsNamed;
    2. if (handler != null) handler();
     
  39. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    I'm curious about that code...I assume it makes a copy of the handler, rather than referencing it? Is that copy allocated on the heap, or the stack? To the profiler!
     
  40. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    delegates are reference types,

    Creating a new reference to a function gets allocated on the heap, like all reference types.

    Setting a new var to an existing delegate, does not create a copy of the object, but rather references the same object.
     
  41. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Yes, delegates are reference types...an event handler, as I understand it, isn't just a delegate, which is why we use the event keyword rather than just declaring a delegate. I had assumed that event handlers are referenced, but for that code to work as I imagine it does, I would think that it's creating a copy of the handler in some way. Otherwise, I can't wrap my head around how that code would accomplish its desired purpose.

    No need to spell out delegates like I'm a novice, thanks.
     
  42. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    The 'event' keyword really is just an access modifier of a delegate.

    Let's take this declaration of an event:

    Code (csharp):
    1.  
    2. public event System.EventHandler OnTrigger;
    3.  
    Note System.EventHandler is just a delegate type:
    https://msdn.microsoft.com/en-us/library/system.eventhandler(v=vs.110).aspx

    You COULD just say:

    Code (csharp):
    1.  
    2. public Sysetm.EventHandler OnTrigger;
    3.  
    Just as easily, and it'll work.

    The difference between the two is the 'event' signals to the compiler that only the class in which the event is defined can invoke it, while all other code can only register to it.

    class defining gets actions:
    OnTrigger += ...;
    OnTrigger -= ...;
    OnTrigger();
    OnTrigger = null;

    all other types get actions:
    OnTrigger += ...;
    OnTrigger -= ...;

    That's all 'event' does.

    It's kind of like a public getter, private setter, type situation for delegates.

    Furthermore, there is actually a property like definition for them:
    https://msdn.microsoft.com/en-us/library/cc713648.aspx

    This example here shows doing it as a interface implementation, but it's legal syntax for anything. You could have private events that are uncovered this way, you could composite events from objects referenced for forwarding, you could perform contextual logic before registering the event.

    But yeah... events are basically access modifiers to delegates.


    You ASKED if they were allocated on the heap. So I told you if they were.

    Sorry if I answered your question!

    Or am I misunderstanding all those question marks?

    Ok, sorry, that may have been snarky... maybe I misunderstood your original question?
     
    Last edited: Dec 13, 2016
  43. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    If this were MS .NET with immutable arrays storing the delegate invocation list, I'd be inclined to agree. With the Mono implementation described earlier, I'm not so certain I misdiagnosed the issue. I'll try to remember to dig up that project and if it isn't too much work I'll undo the fix and take a closer look.

    Event also lets you use delegates in interfaces and the add/remove accessors can be modified. (Edit: Ah I see that's what you meant by the getter/setter comment.)
     
    lordofduct likes this.
  44. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    yep, that's in my post
     
  45. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    We have prohibited the use of "On" in our names, because they're so often misused as event names, or even delegates. At best, On can be the name of a handler (although we've abandoned that in practice), but not the event itself, and certainly never the underlying delegate signature. We follow MS Event Naming guidelines which suggest that the events should be named as a verb (Triggered, Clicked, Added, etc), and the handlers should be named to suggest the event that they handle (HandleThingTriggered(), or OnThingTriggered()). If a custom delegate signature is useful, then it should describe the event being fired and be followed with *Handler (TriggeredHandler, or ThingTriggeredHandler).

    Code (csharp):
    1. class Thing
    2. {
    3.     public delegate void TriggeredHandler(Thing triggeredThing);
    4.  
    5.     public event TriggeredHandler Triggered;
    6.  
    7.     private void NotifyTriggered(Thing thing)
    8.     {
    9.         var handler = Triggered;
    10.         if (handler != null) handler(thing);
    11.     }
    12.  
    13.     private void SubscribeAndFire()
    14.     {
    15.         Triggered += HandleTriggered;
    16.         NotifyTriggered(this);
    17.     }
    18.  
    19.     // "OnTriggered" would be acceptable here, but none of us do that anymore
    20.     private void HandleTriggered(Thing triggeredThing)
    21.     {
    22.         // ...
    23.     }
    24. }
    The reason why we promote the above template is because I once saw a single class with all 3 instances of On* used in different contexts. It looked like
    Code (csharp):
    1. class BadExampleClass {
    2.     public event System.Action OnChange;
    3.  
    4.     // this is an event handler
    5.     private void OnSomeEvent()
    6.     {
    7.         // handle event
    8.     }
    9.  
    10.     // this is an event notifier/invocator
    11.     private void OnSomeOtherEvent()
    12.     {
    13.         if (SomeOtherEvent != null) SomeOtherEvent();
    14.     }
    15. }
    Ugh. Ugly, confusing, inconsistent. I advise to avoid On* because developers rarely agree on exactly how it should be used.
     
  46. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    Yeah, I've heard of this with some teams.

    I often ban it in my business software as well.

    But not in my gamedev, because I often have Triggers (like UnityEvent) that my artists wire into. And they REALLY like the 'On' prefix when looking at the inspector. And honestly... I often bend to the whims of my artists for names of things that are artist facing.

    Anything to make their job easier so they're not showing up asking where this or that is because they don't understood the 'logical'ness of my naming systems.

    As you can see here in my Trigger class the event is 'TriggerActivated', which is code facing:
    https://github.com/lordofduct/space...lob/master/SpacepuppyBase/Scenario/Trigger.cs

    but when the Trigger is used for events that are inspector facing it gets the 'on' prefix:
    https://github.com/lordofduct/space...aster/SpacepuppyBase/Scenario/i_TweenValue.cs
     
    Last edited: Dec 13, 2016
  47. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    I guess the Mono link-list approach is immutable too, so I probably did come to the wrong conclusion. From the docs:

    "Delegates are immutable; once created, the invocation list of a delegate does not change."

    https://developer.xamarin.com/api/type/System.Delegate/

    I haven't been in the habit of looking at the Mono docs (old habits die hard) but they mention the link-list approach right there in the multicast docs:

    https://developer.xamarin.com/api/type/System.MulticastDelegate/
     
  48. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Sorry for the defensive response.
    You did sort of misunderstand my original question. My question was centered around why this code would prevent a delegate removed from an invocation list being called while invoking that list is in process:
    Code (csharp):
    1. var handler = WhateverYourEventIsNamed;
    2. if (handler != null) handler();
    3.  
    My confusion stemmed from the fact that what appears to being going on here (gathering from the stated purpose of the code) is that this somehow makes a local copy of an event, which violated my understanding of delegates as reference types. After digging into it more, I have a better understanding of what's going on under the hood here.
     
    lordofduct likes this.
  49. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    Ohhhh, yeah, I see what you're talking about now.

    Yeah... so yeah, in that situation 'handler' doesn't become a copy of 'WhateverYourEventIsNamed'.

    But rather that if someone registers a new handler with 'WhateverYourEventIsNamed', because delegates are immutable, a new delegate is created at 'WhateverYourEventIsNamed'. Making 'handler' and 'WhateverYourEventIsNamed' different objects, if and only if a new handler is registered.

    That is a thinker though... not quite obvious when you look at it.
     
    Last edited: Dec 14, 2016
    Dameon_ likes this.
  50. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Yup, immutable types were one of those things I was peripherally aware of, but never really understood or used or thought about...always glad to pick up something new from forums!