locked
Passing a function as a parameter RRS feed

  • Question

  • Hey guys,

    I want to pass the method AddTwoNumbers to DisplayAnswer.
    I've looked at some resources online and it's recommended that I use delegates for this task.

    Can someone advise how I'd edit the following code for that?

    Really appreciate any help on the matter.

    ----------------------------------------------------------------------------

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace Calculator
    {
        class Program
        {
            private decimal _numberOne;
            private decimal _numberTwo;

            private decimal _answer;
            private int _temp;

            public decimal AddTwoNumbers(decimal _numberOne, decimal _numberTwo)        
            {
                _answer = (_numberOne + _numberTwo);
                return _answer;
            }
           

    //I get an error here saying "Identifier expected"       

             public void DisplayAnswer(decimal)
            {
                _temp = Convert.ToInt32(_answer);
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("{0}",_temp.ToString());
                Console.WriteLine("-----------------------------------------");

            }

            public static void Main(string[] args)
            {

                int entryOne = 0;
                int entryTwo = 0;

                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;
                decimal decimalResult;
                
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);

                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);

               Program.DisplayAnswer(Program.AddTwoNumbers(convertedDecimalOne, convertedDecimalTwo));

            }
        }
    }


    Kind regards, -TheOtherSid :)

    Friday, June 8, 2012 5:56 AM

Answers

  • If you still want delegates in order to pass one of the functions, then try this:

      
    public void DisplayAnswer( Funcdecimaldecimaldecimal > func, decimal numberOne, decimal numberTwo )
    {
             decimal answer = func( numberOne, numberTwo );
             Console.WriteLine( answer );
    }

    Usage:

    DisplayAnswer( AddTwoNumbers, convertedDecimalOne, convertedDecimalTwo );


    • Edited by Viorel_MVP Friday, June 8, 2012 6:45 AM
    • Proposed as answer by Mr. Javaman II Friday, June 8, 2012 4:18 PM
    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 8:57 AM
    Friday, June 8, 2012 6:44 AM
  • I think you're getting a few things confused here as you've got only one class with some static and instance methods.

    I've created a Displayer and a Calculator class and moved the repective methods to these to illustrate what you want to do.  I think this makes it easier to see the flexibility you can get when you pass functions and how to use delegates.I've then added my own MultiplyTwoNumbers method which very easily then fits into the framework without you needing to make any changes.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    namespace Calculator
    {
        public delegate decimal CalculationHandler(decimal _numberOne, decimal _numberTwo);
     
        class Program
        {
         
    //I get an error here saying "Identifier expected"       
     
          
     
            public static void Main(string[] args)
            {
     
                int entryOne = 0;
                int entryTwo = 0;
     
                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;
                decimal decimalResult;
                
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);
     
                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);
     
                Calculator calculator = new Calculator();
                CalculationHandler additionHandler = calculator.AddTwoNumbers;
     
                Displayer displayer = new Displayer();
                displayer.DisplayAnswer(additionHandler, convertedDecimalOne, convertedDecimalTwo);
     
                //can see the flexibility!
                CalculationHandler multiplicationHandler = calculator.MultiplyTwoNumbers;
                displayer.DisplayAnswer(multiplicationHandler, convertedDecimalOne, convertedDecimalTwo);
     
                Console.ReadLine();
            }
        }
     
        class Calculator
        {
            private decimal _numberOne;
            private decimal _numberTwo;
     
            private decimal _answer;
            private int _temp;
     
            public decimal AddTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne + _numberTwo);
                return _answer;
            }
     
            public decimal MultiplyTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne * _numberTwo);
                return _answer;
            }
        }
     
        class Displayer
        {
            public void DisplayAnswer(CalculationHandler calculationHandler, decimal one, decimal two)
            {
                var answer = calculationHandler(one, two);
                int temp = Convert.ToInt32(answer);
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("{0}", temp.ToString());
                Console.WriteLine("-----------------------------------------");
            }
        }
    }

    There are a few things you could tidy up but this is to illustrate dynamically using different functions via delegates.

    Chris

    • Proposed as answer by Mr. Javaman II Friday, June 8, 2012 4:19 PM
    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 8:57 AM
    Friday, June 8, 2012 2:54 PM
  • Your methods for printing each of the various operations, as you have noticed, has a lot of repetition but with enough differences scattered throughout that you can't just move entire sections into a helper method.

    What you need here is a new class that can represent the variations as variables, that way you can have a single method that uses those variables, called several times with different values.

    Let's take a look at what you're printing out:

    Console.WriteLine("-----------------------------------------");
    Console.WriteLine("--------------Addition-------------------");
    Console.WriteLine("---Processing {0} + {1}------------------", _numberOne, _numberTwo);
    Console.WriteLine("The Sum is {0}", _answer.ToString());
    Console.WriteLine("-----------------------------------------");
    Console.ReadLine();

    So first off, we have "Addition".  This can clearly be held in a string variable; I call it "Title".  You could rename it to "OperationName" possibly, or whatever else seems appropriate to you.

    On the next line the only thing that varies is the '+' character, so we'll want a variable to represent the character of the operation itself, I call it "OperationCharacter".

    On the line after that we have "Sum".  I'm not quite sure of the appropriate term for what it is, but I settled on "ResultType" which I'm still not that fond of.  Not sure if you can come up with a better name.

    And of course, the most important aspect of this MathematicalOperation class is the operation itself.  You were right to think that a Delegate is a good idea here, because it is.  We'll have our last variable for this type be a Delegate that takes two decimals and returns a decimal.  Now, rather than creating our own custom delegate, .NET provides us with "Func" and "Action" as of 3.5, and you can just use those existing delegates for 99% of your delegate needs since they are generic.

    Rather than using public fields in this class to store the data, or having quite a lot of parameters in a constructor, I just used properties.  If you would prefer to change this to using fields instead it wouldn't change how it functions much at all, but using properties here is considered a better practice.

    We'll also need to have a method to actually perform our operation, which can then use all of the variables we've defined here.

    public class MathematicalOperation
    {
        //this is just here for convenience
        private const string spacer = "-----------------------------------------";
    
        public string Title { get; set; } //i.e. Addition, subtraction
        public string ResultType { get; set; } // i.e. Sum, Difference
        public string OperationCharacter { get; set; } //i.e. +, -
        public Func<decimal, decimal, decimal> Function { get; set; }
    
        public string PerformOperation(decimal first, decimal second)
        {
            StringBuilder output = new StringBuilder();
            output.AppendLine(spacer);
    
            output.AppendLine(("---" + Title).PadRight(spacer.Length, '-'));
            output.AppendLine(string.Format("---Processing {0} {1} {2}", first, OperationCharacter, second)
                .PadRight(spacer.Length, '-'));
            output.AppendLine(string.Format("---The {0} is {1}", ResultType, Function(first, second))
                .PadRight(spacer.Length, '-'));
    
            output.AppendLine(spacer);
    
            return output.ToString();
        }
    }

    There are a few things to note in the perform operation method.  

    Rather than having this method print out to the console directly I have it just build up a string and return that.  This way we can either write out the whole thing to the console, or to a file, or both, or anything else we want with it.  

    Rather than contacting lots of strings together I create a StringBuilder, append each line to that, and then have it give me the entire result as a single string.  This is mostly an efficiency thing; it saves a lot on memory operations over simple string concatenation.

    Take note of the use of "PadRight" to ensure that all of the strings are the same length, even though we don't know the size of the content on the line in advance.  This is a very useful method when trying to write out content and have it look nice in the console or a file.

    Next we'll define a few simple functions:

    public static decimal Add(decimal first, decimal second)
    {
        return first + second;
    }
    
    public static decimal Multiply(decimal first, decimal second)
    {
        return first * second;
    }

    Those can go in Program.cs, in your own Math library, etc.  They probably should not go in in the MathematicalOperation class.

    And finally the code to actually execute all of this:

    MathematicalOperation additionOp = new MathematicalOperation();
    additionOp.Title = "Addition";
    additionOp.ResultType = "Sum";
    additionOp.OperationCharacter = "+";
    additionOp.Function = Add;
    
    MathematicalOperation multipicationOp = new MathematicalOperation();
    multipicationOp.Title = "Multiplication";
    multipicationOp.ResultType = "Product";
    multipicationOp.OperationCharacter = "x";
    multipicationOp.Function = Multiply;
    
    System.Console.WriteLine(additionOp.PerformOperation(2, 3));
    System.Console.WriteLine();
    System.Console.WriteLine(multipicationOp.PerformOperation(2, 3));

    And Voila.  Much, much less duplication of code.

    You've also discussed the possibility of having the user input which operation they would like to perform.  This can fit into that model quite well.  At the start of your program create all of the MathematicalOperation instances that you want to support (Add, subtract, etc.) and then put them all into a Dictionary.  The key for the dictionary could be what the user enters for the operation that they want ('A' for Addition, 'S' for subtraction).  The creation of the dictionary could look something like this:

    Dictionary<string, MathematicalOperation> operationMap = new Dictionary<string, MathematicalOperation>();
    operationMap.Add("A", additionOp);

    After that, you can read in the operation from the user, use the ContainsKey method to determine if it's a valid choice (and re-prompt if it's not), and then get the appropriate MathematicalOperation instance for that key if it's valid.  At this point you can use the "PerformOperation" method on whatever the Dictionary spits out and you know it will do what the user wants.  

    If you would like me to go into some aspect of this definition in more detail, or you are having trouble implementing some bit that I have left unimplemented, please ask for further clarification.

    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 9:01 AM
    Friday, June 8, 2012 4:47 PM
  • Try this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace calculator
    {
        class Program
        {
            private static decimal _numberOne;
            private static decimal _numberTwo;
    
            private static decimal _answer;
            private static int _temp;
    
            public delegate decimal AddNumbers(decimal _numberOne, decimal _numberTwo);
    
            public static decimal AddTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne + _numberTwo);
                return _answer;
            }
    
            public static void DisplayAnswer(int entryOne, int entryTwo, AddNumbers m)
            {
                _answer = m(entryOne, entryTwo);
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("{0}", _answer.ToString());
                Console.WriteLine("-----------------------------------------");
            }
    
            public static void Main(string[] args)
            {
                int entryOne = 0;
                int entryTwo = 0;
    
                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;
                decimal decimalResult;
    
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);
    
                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);
    
                DisplayAnswer(entryOne, entryTwo, AddTwoNumbers);
            }
        }
    }

    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 9:09 AM
    Friday, June 8, 2012 6:22 AM

All replies

  • change this line :   public void DisplayAnswer(decimal)

    to:    public void DisplayAnswer(decimal someNumber)

    Friday, June 8, 2012 6:03 AM
  • Hey Pantelis,

    That created 2 new errors:

    - An object reference is required for the non-static field, method, or property 'Calculator.Program.DisplayAnswer(decimal)'

    - An object reference is required for the non-static field, method, or property 'Calculator.Program.AddTwoNumbers(decimal, decimal)'

    Thanks.


    Kind regards, -TheOtherSid :)

    Friday, June 8, 2012 6:10 AM
  • Try this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace calculator
    {
        class Program
        {
            private static decimal _numberOne;
            private static decimal _numberTwo;
    
            private static decimal _answer;
            private static int _temp;
    
            public delegate decimal AddNumbers(decimal _numberOne, decimal _numberTwo);
    
            public static decimal AddTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne + _numberTwo);
                return _answer;
            }
    
            public static void DisplayAnswer(int entryOne, int entryTwo, AddNumbers m)
            {
                _answer = m(entryOne, entryTwo);
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("{0}", _answer.ToString());
                Console.WriteLine("-----------------------------------------");
            }
    
            public static void Main(string[] args)
            {
                int entryOne = 0;
                int entryTwo = 0;
    
                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;
                decimal decimalResult;
    
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);
    
                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);
    
                DisplayAnswer(entryOne, entryTwo, AddTwoNumbers);
            }
        }
    }

    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 9:09 AM
    Friday, June 8, 2012 6:22 AM
  • Hi,

    you have complicated Too much with your simple code. What would it be if you would start with some more compliacted project . Kidding :)

    I repaired your code, through out some unnecessary lines, and keept the necessary ones.

    Here it is:

        class Program
        {
            static decimal _numberOne; //this is considered as private -when nothing specified!
            static decimal _numberTwo;
            static decimal _answer;
    
            public static void AddTwoNumbers()
            {
                _answer = (_numberOne + _numberTwo);
            }
    
            public static void DisplayAnswer()
            {
                Console.WriteLine("-----------------------------------------");
                AddTwoNumbers();
                Console.WriteLine("{0}", _answer);
                Console.WriteLine("-----------------------------------------");
    
            }
    
            public static void Main(string[] args)
            {
                Console.WriteLine("Enter the first value: ");
                _numberOne = Convert.ToDecimal(Console.ReadLine());
    
                Console.WriteLine("Enter the second value: ");
                _numberTwo = Convert.ToDecimal(Console.ReadLine());
    
                DisplayAnswer();
                Console.ReadLine();
            }
        }

    And btw, no need to mark fields as private, because if you dont mark them at all, that means thay are private!!


    Mitja

    Friday, June 8, 2012 6:24 AM
  • You guys ROCK!

    I kept reading and re-reading my code. And I realised that I hadn't even declared the object <FacePalm>

    I changed the code a bit to this....

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace Calculator
    {
        class Program
        {
            decimal _numberOne;
            decimal _numberTwo;

            decimal _answer;
            int _temp;

            public void AddTwoNumbers(decimal _numberOne, decimal _numberTwo)        
            {
                _answer = (_numberOne + _numberTwo);
                _temp = Convert.ToInt32(_answer);

                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("The Answer is {0}", _temp.ToString());
                Console.WriteLine("-----------------------------------------");
                Console.ReadLine();
            }

            public static void Main(string[] args)
            {

                int entryOne = 0;
                int entryTwo = 0;

                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;

                
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);

                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);

                Program UmadBro = new Program();
                UmadBro.AddTwoNumbers(convertedDecimalOne, convertedDecimalTwo);
            }
        }
    }

    Next, I'm going to make it simpler (hopefully) by implementing Delegates (more reading to do on that)


    Kind regards, -TheOtherSid :)

    Friday, June 8, 2012 6:29 AM

  •     class Program
        {
            static decimal _numberOne; //this is considered as private -when nothing specified!
            static decimal _numberTwo;
            static decimal _answer;
    
    

    Mitja

    Hey dude,

    You made the private decimal fields "Static"

    Can you tell me why?
    Seems interesting that you can actually do that.


    Kind regards, -TheOtherSid :)

    Friday, June 8, 2012 6:32 AM
  • If you still want delegates in order to pass one of the functions, then try this:

      
    public void DisplayAnswer( Funcdecimaldecimaldecimal > func, decimal numberOne, decimal numberTwo )
    {
             decimal answer = func( numberOne, numberTwo );
             Console.WriteLine( answer );
    }

    Usage:

    DisplayAnswer( AddTwoNumbers, convertedDecimalOne, convertedDecimalTwo );


    • Edited by Viorel_MVP Friday, June 8, 2012 6:45 AM
    • Proposed as answer by Mr. Javaman II Friday, June 8, 2012 4:18 PM
    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 8:57 AM
    Friday, June 8, 2012 6:44 AM
  • Hey Viorel,

    I want to have the following functions as part of my code:

    -        public void AddTwoNumbers(decimal _numberOne, decimal _numberTwo)    

    -        public void SubtractionTwoNumbers(decimal _numberOne, decimal _numberTwo)

    -        public void MultiplyTwoNumbers(decimal _numberOne, decimal _numberTwo)

    -        public void DivideTwoNumbers(decimal _numberOne, decimal _numberTwo)

    Would you recommend that I use delegates in my code?
    I also intend on adding two more things to my code (this is a console application)

    - a Menu which corresponds to a Switch Statement (i'm gonna have to use Enums for that)

             - A for Add
             - S for Subtract
             - M for Multiply
             - D for Divide
             - E for Exit (And send all data to a txt file on Desktop)

    - Until the user presses 'E', the console should keep the application running.

    I've managed to finish writing the functions.
    But, as you can see, there's a lot of repetition in the display aspect of the code.

    Which is why I'd like to use Delegates and Enums.

    ------------------------------------------------------------------------------------------------

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace Calculator
    {
        class Program
        {
            decimal _numberOne;
            decimal _numberTwo;

            decimal _answer;

            public void AddTwoNumbers(decimal _numberOne, decimal _numberTwo)        
            {
                _answer = (_numberOne + _numberTwo);

                    Console.WriteLine("-----------------------------------------");
                    Console.WriteLine("--------------Addition-------------------");
                    Console.WriteLine("---Processing {0} + {1}------------------", _numberOne, _numberTwo);
                    Console.WriteLine("The Sum is {0}", _answer.ToString());
                    Console.WriteLine("-----------------------------------------");
                    Console.ReadLine();
            }



            public void SubtractionTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne - _numberTwo);

                    Console.WriteLine("-----------------------------------------");
                    Console.WriteLine("---------------Subtraction---------------");
                    Console.WriteLine("---Processing {0} - {1}------------------", _numberOne, _numberTwo);
                    Console.WriteLine("The Difference is {0}", _answer.ToString());
                    Console.WriteLine("-----------------------------------------");
                    Console.ReadLine();

            }

            public void MultiplyTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne*_numberTwo);

                    Console.WriteLine("-----------------------------------------");
                    Console.WriteLine("------------Multiplication---------------");
                    Console.WriteLine("---Processing {0} x {1}------------------", _numberOne, _numberTwo);
                    Console.WriteLine("The Product is {0}", _answer.ToString());
                    Console.WriteLine("-----------------------------------------");
                    Console.ReadLine();
            }

            public void DivideTwoNumbers(decimal _numberOne, decimal _numberTwo)
             {
                    _answer = (_numberOne/_numberTwo);

                        Console.WriteLine("-----------------------------------------");
                        Console.WriteLine("---------------Division------------------");
                        Console.WriteLine("---Processing {0} / {1}------------------", _numberOne, _numberTwo);
                        Console.WriteLine("The Answer is {0}", _answer.ToString());
                        Console.WriteLine("-----------------------------------------");
                        Console.ReadLine();
              }

            public static void Main(string[] args)
            {
                string getFirstEntry;
                string getSecondEntry;

                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;

                
                Console.WriteLine("Enter the first value: ");
                getFirstEntry = Console.ReadLine();
                convertedDecimalOne = Convert.ToDecimal(getFirstEntry);                    

                Console.WriteLine("Enter the second value: ");
                getSecondEntry = Console.ReadLine();
                convertedDecimalTwo = Convert.ToDecimal(getSecondEntry);

                Program madBro = new Program();

                madBro.AddTwoNumbers(convertedDecimalOne, convertedDecimalTwo);
                madBro.SubtractionTwoNumbers(convertedDecimalOne, convertedDecimalTwo);
                madBro.MultiplyTwoNumbers(convertedDecimalOne, convertedDecimalTwo);
                madBro.DivideTwoNumbers(convertedDecimalOne, convertedDecimalTwo);
            }
        }
    }


    Kind regards, -TheOtherSid :)

    Friday, June 8, 2012 7:22 AM
  • Because of your replies I get the idea that this is something interesting for you.

    Be aware not my hobby (Operator overloading)

    http://msdn.microsoft.com/en-us/library/8edha89s.aspx


    Success
    Cor


    Friday, June 8, 2012 7:29 AM
  • I think you're getting a few things confused here as you've got only one class with some static and instance methods.

    I've created a Displayer and a Calculator class and moved the repective methods to these to illustrate what you want to do.  I think this makes it easier to see the flexibility you can get when you pass functions and how to use delegates.I've then added my own MultiplyTwoNumbers method which very easily then fits into the framework without you needing to make any changes.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    namespace Calculator
    {
        public delegate decimal CalculationHandler(decimal _numberOne, decimal _numberTwo);
     
        class Program
        {
         
    //I get an error here saying "Identifier expected"       
     
          
     
            public static void Main(string[] args)
            {
     
                int entryOne = 0;
                int entryTwo = 0;
     
                decimal convertedDecimalOne;
                decimal convertedDecimalTwo;
                decimal decimalResult;
                
                Console.WriteLine("Enter the first value: ");
                entryOne = Convert.ToInt32(Console.ReadLine());
                convertedDecimalOne = Convert.ToDecimal(entryOne);
     
                Console.WriteLine("Enter the second value: ");
                entryTwo = Convert.ToInt32(Console.ReadLine());
                convertedDecimalTwo = Convert.ToDecimal(entryTwo);
     
                Calculator calculator = new Calculator();
                CalculationHandler additionHandler = calculator.AddTwoNumbers;
     
                Displayer displayer = new Displayer();
                displayer.DisplayAnswer(additionHandler, convertedDecimalOne, convertedDecimalTwo);
     
                //can see the flexibility!
                CalculationHandler multiplicationHandler = calculator.MultiplyTwoNumbers;
                displayer.DisplayAnswer(multiplicationHandler, convertedDecimalOne, convertedDecimalTwo);
     
                Console.ReadLine();
            }
        }
     
        class Calculator
        {
            private decimal _numberOne;
            private decimal _numberTwo;
     
            private decimal _answer;
            private int _temp;
     
            public decimal AddTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne + _numberTwo);
                return _answer;
            }
     
            public decimal MultiplyTwoNumbers(decimal _numberOne, decimal _numberTwo)
            {
                _answer = (_numberOne * _numberTwo);
                return _answer;
            }
        }
     
        class Displayer
        {
            public void DisplayAnswer(CalculationHandler calculationHandler, decimal one, decimal two)
            {
                var answer = calculationHandler(one, two);
                int temp = Convert.ToInt32(answer);
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("{0}", temp.ToString());
                Console.WriteLine("-----------------------------------------");
            }
        }
    }

    There are a few things you could tidy up but this is to illustrate dynamically using different functions via delegates.

    Chris

    • Proposed as answer by Mr. Javaman II Friday, June 8, 2012 4:19 PM
    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 8:57 AM
    Friday, June 8, 2012 2:54 PM
  • Your methods for printing each of the various operations, as you have noticed, has a lot of repetition but with enough differences scattered throughout that you can't just move entire sections into a helper method.

    What you need here is a new class that can represent the variations as variables, that way you can have a single method that uses those variables, called several times with different values.

    Let's take a look at what you're printing out:

    Console.WriteLine("-----------------------------------------");
    Console.WriteLine("--------------Addition-------------------");
    Console.WriteLine("---Processing {0} + {1}------------------", _numberOne, _numberTwo);
    Console.WriteLine("The Sum is {0}", _answer.ToString());
    Console.WriteLine("-----------------------------------------");
    Console.ReadLine();

    So first off, we have "Addition".  This can clearly be held in a string variable; I call it "Title".  You could rename it to "OperationName" possibly, or whatever else seems appropriate to you.

    On the next line the only thing that varies is the '+' character, so we'll want a variable to represent the character of the operation itself, I call it "OperationCharacter".

    On the line after that we have "Sum".  I'm not quite sure of the appropriate term for what it is, but I settled on "ResultType" which I'm still not that fond of.  Not sure if you can come up with a better name.

    And of course, the most important aspect of this MathematicalOperation class is the operation itself.  You were right to think that a Delegate is a good idea here, because it is.  We'll have our last variable for this type be a Delegate that takes two decimals and returns a decimal.  Now, rather than creating our own custom delegate, .NET provides us with "Func" and "Action" as of 3.5, and you can just use those existing delegates for 99% of your delegate needs since they are generic.

    Rather than using public fields in this class to store the data, or having quite a lot of parameters in a constructor, I just used properties.  If you would prefer to change this to using fields instead it wouldn't change how it functions much at all, but using properties here is considered a better practice.

    We'll also need to have a method to actually perform our operation, which can then use all of the variables we've defined here.

    public class MathematicalOperation
    {
        //this is just here for convenience
        private const string spacer = "-----------------------------------------";
    
        public string Title { get; set; } //i.e. Addition, subtraction
        public string ResultType { get; set; } // i.e. Sum, Difference
        public string OperationCharacter { get; set; } //i.e. +, -
        public Func<decimal, decimal, decimal> Function { get; set; }
    
        public string PerformOperation(decimal first, decimal second)
        {
            StringBuilder output = new StringBuilder();
            output.AppendLine(spacer);
    
            output.AppendLine(("---" + Title).PadRight(spacer.Length, '-'));
            output.AppendLine(string.Format("---Processing {0} {1} {2}", first, OperationCharacter, second)
                .PadRight(spacer.Length, '-'));
            output.AppendLine(string.Format("---The {0} is {1}", ResultType, Function(first, second))
                .PadRight(spacer.Length, '-'));
    
            output.AppendLine(spacer);
    
            return output.ToString();
        }
    }

    There are a few things to note in the perform operation method.  

    Rather than having this method print out to the console directly I have it just build up a string and return that.  This way we can either write out the whole thing to the console, or to a file, or both, or anything else we want with it.  

    Rather than contacting lots of strings together I create a StringBuilder, append each line to that, and then have it give me the entire result as a single string.  This is mostly an efficiency thing; it saves a lot on memory operations over simple string concatenation.

    Take note of the use of "PadRight" to ensure that all of the strings are the same length, even though we don't know the size of the content on the line in advance.  This is a very useful method when trying to write out content and have it look nice in the console or a file.

    Next we'll define a few simple functions:

    public static decimal Add(decimal first, decimal second)
    {
        return first + second;
    }
    
    public static decimal Multiply(decimal first, decimal second)
    {
        return first * second;
    }

    Those can go in Program.cs, in your own Math library, etc.  They probably should not go in in the MathematicalOperation class.

    And finally the code to actually execute all of this:

    MathematicalOperation additionOp = new MathematicalOperation();
    additionOp.Title = "Addition";
    additionOp.ResultType = "Sum";
    additionOp.OperationCharacter = "+";
    additionOp.Function = Add;
    
    MathematicalOperation multipicationOp = new MathematicalOperation();
    multipicationOp.Title = "Multiplication";
    multipicationOp.ResultType = "Product";
    multipicationOp.OperationCharacter = "x";
    multipicationOp.Function = Multiply;
    
    System.Console.WriteLine(additionOp.PerformOperation(2, 3));
    System.Console.WriteLine();
    System.Console.WriteLine(multipicationOp.PerformOperation(2, 3));

    And Voila.  Much, much less duplication of code.

    You've also discussed the possibility of having the user input which operation they would like to perform.  This can fit into that model quite well.  At the start of your program create all of the MathematicalOperation instances that you want to support (Add, subtract, etc.) and then put them all into a Dictionary.  The key for the dictionary could be what the user enters for the operation that they want ('A' for Addition, 'S' for subtraction).  The creation of the dictionary could look something like this:

    Dictionary<string, MathematicalOperation> operationMap = new Dictionary<string, MathematicalOperation>();
    operationMap.Add("A", additionOp);

    After that, you can read in the operation from the user, use the ContainsKey method to determine if it's a valid choice (and re-prompt if it's not), and then get the appropriate MathematicalOperation instance for that key if it's valid.  At this point you can use the "PerformOperation" method on whatever the Dictionary spits out and you know it will do what the user wants.  

    If you would like me to go into some aspect of this definition in more detail, or you are having trouble implementing some bit that I have left unimplemented, please ask for further clarification.

    • Marked as answer by Lisa Zhu Wednesday, June 13, 2012 9:01 AM
    Friday, June 8, 2012 4:47 PM