question

Lancis007-9977 avatar image
0 Votes"
Lancis007-9977 asked Bruce-SqlWork answered

Convert from Old delegate to Action/Func delegate

Hi everybody, the below code is working fine, I'm busy learning delegates and I'm now learning Func and Action.

I just want to see how the multicast delegate works with Func and Action cause most of books I read about Action and Func doesn't use them in multicast way.

Because I noticed that Action and Func delegate doesn't work well in multicast, if you plug two or multiple methods, it only displays data from the last plugged method.
that's why I want to see how you guys going to achieve multicast with Action and Func using the below class.

You can also comment out all if statement inside the 3 methods to see how the old multicast delegate display all the 3 methods data but the doing the same thing with Action/Func seems like it's not possible.

Not sure, but let see how you're gonna achieve it.


Can you please assist me converting this below code from old delegate to Action, then Func delegate ?


I'm working on VS 2019, .Net Core.


 namespace DelegateGreetingApp
 {
     // 1. 
     delegate void GreetingDelegate();
    
     class Program
     {
         static void Main(string[] args)
         {
             GreetingDelegate greet = new GreetingDelegate(GoodMorning);
             greet += GoodAfternoon;
             greet += GoodEvening;
    
             greet();
    
    
             Console.Read();
         }
    
         static void GoodMorning()
         {
             //if (DateTime.Now.ToString().EndsWith("PM"))
    
             if (DateTime.Now.Hour > 4 && DateTime.Now.Hour < 12)
                 Console.WriteLine("Good Morning");
         }
    
         static void GoodAfternoon()
         {
             //if (DateTime.Now.ToString().EndsWith("PM"))
    
             if(DateTime.Now.Hour > 11 && DateTime.Now.Hour < 17)
                 Console.WriteLine("Good Afternoon");
         }
    
         static void GoodEvening()
         {
             if (DateTime.Now.Hour > 16 || DateTime.Now.Hour < 5)
                 Console.WriteLine("Good Evening");
         }
     }
 }



  • Good Morning

  • ------------

  • 5:00 AM — 11:59 AM

  • Good Afternoon

  • --------------

  • 12:00 PM — 4:59 PM

  • Good Evening

  • ------------

  • 5:00 PM — 4:59 AM*



dotnet-csharpdotnet-aspnet-core-generaldotnet-standard
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Paul-5034 avatar image
0 Votes"
Paul-5034 answered Paul-5034 edited

I've simplified the implementation from the methods for brevity, but functionality-wise multicasting a delegate defined with the delegate keyword will be as intuitive as multicasting with the equivalent Action or Func type.

One thing to note is that if your GreetingDelegate had a return value rather than void, making it functionally equivalent to a Func, multicasting isn't especially useful if your goal is to use the return value of each call. The default behaviour is that when you call "greetFunc" below it will execute each of the added functions in series, but the return value with be the value returned by the last function added to the multicast list, so "3".

It is possible to loop through each function individually, although that does make multicasting somewhat pointless:

 foreach (Func<int> func in greetFunc.GetInvocationList()) {
     Console.WriteLine(func());
 }


Updated source:

 using System;
    
 namespace DelegateGreetingApp {
     // 1. 
     delegate void GreetingDelegate();
    
     class Program {
         static void Main(string[] args) {
             // 1. Multicast delegates
             GreetingDelegate greetDelegate = new GreetingDelegate(GoodMorning);
             greetDelegate += GoodAfternoon;
             greetDelegate += GoodEvening;
             greetDelegate();
    
             Console.WriteLine("---");
    
             // 2. Multicast delegates (using Action)
             Action greetAction = GoodMorning;
             greetAction += GoodAfternoon;
             greetAction += GoodEvening;
             greetAction();
    
             Console.WriteLine("---");
    
             // 3. Multicast delegates (using Func)
             Func<int> greetFunc = () => {
                 GoodMorning();
                 return 1;
             };
             greetFunc += () => {
                 GoodAfternoon();
                 return 2;
             };
             greetFunc += () => {
                 GoodEvening();
                 return 3;
             };
             Console.WriteLine(greetFunc());
    
             Console.Read();
         }
    
         static void GoodMorning() {
             //if (DateTime.Now.ToString().EndsWith("PM"))
    
             if (DateTime.Now.Hour > 4 && DateTime.Now.Hour < 12)
                 Console.WriteLine("Good Morning");
         }
    
         static void GoodAfternoon() {
             //if (DateTime.Now.ToString().EndsWith("PM"))
    
             if (DateTime.Now.Hour > 11 && DateTime.Now.Hour < 17)
                 Console.WriteLine("Good Afternoon");
         }
    
         static void GoodEvening() {
             if (DateTime.Now.Hour > 16 || DateTime.Now.Hour < 5)
                 Console.WriteLine("Good Evening");
         }
     }
 }


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Lancis007-9977 avatar image
0 Votes"
Lancis007-9977 answered Paul-5034 commented

@Paul-5034 Thanks for the code.

But, in my above class, within each method there's a condition inside that check the current time and run a specific method accordingly. Your code only displays the all 3 methods directly without checking against the condition.

I wanted a multicast Func and Action that run a specific method according to the current time. I updated my post to add the schedule.

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

That was more of a demonstration, but I've updated my answer so it assigns your methods to greetAction instead of creating inline actions. As your GoodMorning/GoodAfternoon/GoodEvening methods don't have return values I've just added inline functions that return an integer after calling your methods.

1 Vote 1 ·
Lancis007-9977 avatar image
0 Votes"
Lancis007-9977 answered Lancis007-9977 commented

I see the updated code. It's now working fine using Action. Thank you !!!


So is this a good design or bad design ?

If you had that requirement and were to design and write an application to show Morning/Afternoon/Evening according to the above time schedule using delegate Action or Func, how would you go write it?

I just want to see how advanced developers think.

I also read on C# Official doc that delegates promote Single Responsibility and Separation of Concern principle, so I decided to use it everywhere.



· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Well Morning/Afternoon/Evening are mutually exclusive, so if it were me I'd be tempted to use a function to get the correct greeting (rather than 3 separate methods), and then an action to print it.

 public enum Greeting {
     Morning, Afternoon, Evening
 }
    
 Func<Greeting> GetGreeting = () => {
     if (DateTime.Now.Hour > 4 && DateTime.Now.Hour < 12)
         return Greeting.Morning;
     if (DateTime.Now.Hour > 11 && DateTime.Now.Hour < 17)
         return Greeting.Afternoon;
     if (DateTime.Now.Hour > 16 || DateTime.Now.Hour < 5)
         return Greeting.Evening;
    
     throw new Exception("Failed to get greeting for current hour");
 };

This way you're eliminating the possibility of more than one of your greeting methods from printing as once, as your code expects one greeting.

I've split out the "get greeting" and "print greeting" specifically because you mention single responsibility & separation of concerns. Here's the print action:

 Action<Greeting> PrintGreeting = greeting => Console.WriteLine($"Good {greeting}");
1 Vote 1 ·

I like the way you split Action and Func.
I really learnt a lot on this post.

Thank you again Paul.

0 Votes 0 ·
Lancis007-9977 avatar image
0 Votes"
Lancis007-9977 answered Paul-5034 commented

@Paul-5034 Thanks, that's awesome and clean.

Thank you so much Paul.

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

No problem

0 Votes 0 ·
Bruce-SqlWork avatar image
0 Votes"
Bruce-SqlWork answered

Action and Func are generic delegates. you could skip the delegate definition and just use an action:

      public class Program
      {
          static void Main(string[] args)
          {
              Action greet = GoodMorning;
                 
              greet += GoodAfternoon;
              greet += GoodEvening;
              greet += () => {
                Console.WriteLine("hello");
              };
              greet();
        
        
              Console.Read();
          }
    
          ....
 }
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.