The Tasks of an Object

A Task as a Type

Introduction

As seen in the previous lesson, a task is created from a class named Task<> (or Task). This means that a task is primarily an (electronic) object. This also means that a Task<> object can be used like any normal class.

Practical LearningPractical Learning: Introducing Tasks Objects

  1. Start Microsoft Visual Studio
  2. Ceate a Windows Forms App named TrafficTicketsManagement2
  3. In the Solution Explorer, right-click Form1.cs and click Rename
  4. Type TrafficSystem (to get TrafficSystem.cs)
  5. Press Enter twice to accept the name and display the form
  6. Design the form as follows:

    he Tasks of an Object - Introduction to Tasks

    Control (Name): Text Other Properties
    GroupBox   Speed Ticket  
    Label   Speed Limit:  
    TextBox txtSpeedLimit   TextAlign: Right
    Label   Driver Speed:  
    TextBox txtDriverSpeed   TextAlign: Right
    Button btnSpeedTicketAmount Ticket Amount  
    TextBox txtSpeedTicketAmount   TextAlign: Right
    GroupBox   Police Stop  
    Label   Police Stop Reason:  
    ComboBox cbxPoliceStops   Items:
    Other
    Tail Light
    Break Light
    Reckless Driving
    Driving Under Influence
    TextBox txtPoliceTicketAmount   TextAlign: Right
    GroupBox   Traffic Light  
    Label   Traffic Light Violation:  
    ComboBox cbxTrafficLightViolation   Items:
    Other
    No Turn on Red
    High Speed Driving
    Red Light Drive Through
    Flashing Light No Stopping
    TextBox txtTrafficLightTicketAmount   TextAlign: Right
  7. On the form, double-click the Ticket Amount button
  8. Return to the form and double-click the Police Stop Reason combo box
  9. Change the document as follows:
    namespace TrafficTicketsManagement2
    {
        public partial class TrafficSystem : Form
        {
            public TrafficSystem()
            {
                InitializeComponent();
            }
    
            (int limit, int driving) speed;
    
            int SetTicketAmountSpeed()
            {
                int speedDifference = speed.driving - speed.limit;
    
                if (speedDifference < 10)
                    return 0;
                else if (speedDifference < 20)
                    return 75;
                else if (speedDifference < 35)
                    return 100;
                else if (speedDifference < 50)
                    return 150;
                else
                    return 225;
            }
    
            private void btnSpeedTicketAmount_Click(object sender, EventArgs e)
            {
                speed.limit = int.Parse(txtSpeedLimit.Text);
                speed.driving = int.Parse(txtDriverSpeed.Text);
    
                Task<int> ticketSpeed = new Task<int>(SetTicketAmountSpeed);
    
                ticketSpeed.Start();
    
                txtSpeedTicketAmount.Text = ticketSpeed.Result.ToString();
            }
    
            private void cbxPoliceStops_SelectedIndexChanged(object sender, EventArgs e)
            {
                Func<int> evaluatePoliceStop = new Func<int>(() =>
                {
                    int amount = 0;
    
                    switch (cbxPoliceStops.Text)
                    {
                        case "Tail Light":
                            amount = 40;
                            break;
                        case "Break Light":
                            amount = 60;
                            break;
                        case "Reckless":
                            amount = 160;
                            break;
                        case "Under Influence":
                            amount = 300;
                            break;
                        case "Other":
                            amount = 25;
                            break;
                        default:
                            amount = 0;
                            break;
                    }
    
                    return amount;
                });
    
                Task<int> processPoliceIssue = Task<int>.Run<int>(evaluatePoliceStop);
    
                txtPoliceTicketAmount.Text = processPoliceIssue.Result.ToString();
            }
        }
    }
  10. To execute, on the main menu, click Debug -> Start Without Debugging:

    The Tasks of an Object - A Task as a Type

  11. In the Speed Limit text box, type 55
  12. In the Driver Speed text box, type 105
  13. Click the Ticket Amount button:

    The Tasks of an Object - A Task as a Type

  14. In the Police Stop Reason combo box, select Break Light

    The Tasks of an Object - A Task as a Type

  15. Close the form and return to your programming environment
  16. Click the TrafficSystem.cs [Design] tab to return to the form
  17. On the form, double-click the Traffic Light Violation combo box

Returning a Task as Object

A functional task is one that runs asynchronously. One of the most important features here is that the function or method may produce an object that is a task. Of course, you must create the function to produce such an object.

To create a method that produces a task, specify its data type as Task<>. Enter the name of the class as the parameter type. Here is an example:

public Task<double> GetInventory()
{
}

As always, before the end of the method, make sure you return an object of type Task<>. You have various options. In the function, you can declare a Task<> and initialize it with a constructor that takes a function. You can externally define that other function. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise() => InitializeComponent();

        string Say()
        {
            return "Alright! Welcome to my world.";
        }

        Task<string> GetInventory()
        {
            Task<string> thought = new Task<string>(Say);

            return thought;
        }
    }
}

Remember that you usually declare a variable only if you are planning to use it. If not, when it comes to a function that returns an object, you can return the instance directly on the return keyword. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise() => InitializeComponent();

        string Say()
        {
            return "Alright! Welcome to my world.";
        }

        Task<string> GetInventory()
        {
            return new Task<string>(Say);
        }
    }
}

Remember that if you have a function that involves a single expression, you can reduce its code to an expression-body. Here are examples:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise() => InitializeComponent();

        string Say() => "Alright! Welcome to my world.";

        Task<string> GetInventory() => new Task<string>(Say);
    }
}

Instead of defining a function externally as we first did, you can define the function as a delegate where it is needed. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise() => InitializeComponent();

        Task<string> GetInventory()
        {
            Task<string> thought = new Task<string>(() =>
            {
                return "Alright! Welcome to my world.";
            });

            return thought;
        }
    }
}

After defining the function that returns a Task<> object, you can use its value. To do that, where the object is needed, declare a variable of type Task<> and initialize it. Then use that variable to start the task.

Practical LearningPractical Learning: Returning a Task

  1. Change the document as follows:
    namespace TrafficTicketsManagement2
    {
        public partial class TrafficSystem : Form
        {
            . . .
    
            private void cbxPoliceStops_SelectedIndexChanged(object sender, EventArgs e)
            {
                . . .
            }
    
            Task<int> SetTraffiLightTicketAmount()
            {
                Task<int> evalluation = new Task<int>(() =>
                {
                    int amount = 0;
    
                    switch (cbxTrafficLightViolation.Text)
                    {
                        case "Other":
                            amount = 100;
                            break;
                        case "No Turn on Red":
                            amount = 45;
                            break;
                        case "Flashing Light No Stopping":
                            amount = 85;
                            break;
                        case "Red Light Drive Through":
                            amount = 225;
                            break;
                        case "High Speed Driving":
                            amount = 75;
                            break;
                        default:
                            amount = 50;
                            break;
                    }
    
                    return amount;
                });
    
                return evalluation;
            }
    
            private void cbxTrafficLightViolations_SelectedIndexChanged(object sender, EventArgs e)
            {
                Task<int> light = SetTraffiLightTicketAmount();
    
                light.Start();
                txtTrafficLightTicketAmount.Text = light.Result.ToString();
            }
        }
    }
  2. To execute, on the main menu, click Debug -> Start Without Debugging:
  3. In the Traffic Light Violation combo box, select High Speed Driving

    The Tasks of an Object - A Task as a Type

  4. Close the form and return to your programming environment

A Task as Argument

Because a task is an object based on a class, a task can be passed as argument. To proceed, when creating the function ot method, in its parentheses, type Task<>, include its parameter type, and follow it with a name for the parameter. In the body of the method, ignore the parameter if you don't need it. Otherwise, you can get the value of the parameter. For example, you can initialize it with a value or a method that produces its type of task. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        void ProcessPrice(Task<float> cost)
        {
        }
    }
}

You can involve the parameter or its value in any operation or processing you want. You can then use the function or method any way you want outside its body. When calling the function, pass a Task<> object. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        (int limit, int driving) speed = (35, 75);

        float SetTicketAmountSpeed()
        {
            int speedDifference = speed.driving - speed.limit;

            if (speedDifference < 10f)
                return 0;
            else if (speedDifference < 20f)
                return 75f;
            else if (speedDifference < 35f)
                return 100f;
            else if (speedDifference < 50f)
                return 150f;
            else
                return 225f;
        }

        void ProcessPrice(Task<float> cost)
        {
            cost.Start();

            MessageBox.Show("Ticket Amount: " + cost.Result.ToString(), "Traffic Tickets Management");
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<float> price = new Task<float>(SetTicketAmountSpeed);

            ProcessPrice(price);
        }
    }
}

This would produce:

A Task as Argument

A Task of an Object Type

Introduction

All the tasks we have created and used so far were producing a primitive type or a string. Indeed, tasks are not limited to value types and strings, a task can produce an object of a class.

A Task that Produces an Object

To make a task produce an object, you first need a class. You have various options. You can first create a class or structure. When creating a task, specify its parameter type as your class or structure. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<SpeedTicket> price = ...;
        }
    }

    class SpeedTicket
    {
    }
}

To initialize a task, you can create a function that returns an object of the class. You can then pass the name of that function to the Task<> constructor. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        SpeedTicket ProcessInfraction()
        {
            return new SpeedTicket()
            {
            };
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<SpeedTicket> cost = new Task<SpeedTicket>(ProcessInfraction);
        }
    }

    public class SpeedTicket
    {
    }
}

After creating and initializing the task, you can start it, by calling the Task.Start()method, excatly as we learned in the previous lesson. To access the values of the object, we saw that you can access the Task.Result property. Because in this case you are dealing with an object (of a class), the Task.Result property actually represents the class. The Intellisense is aware of the public properties of the class and can easily allow you to access each of those properties. Here is an example:

namespace GeneralizedWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        SpeedTicket ProcessInfraction()
        {
            return new SpeedTicket()
            {
                Speed = (30, 48),
                Paid  = false
            };
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<SpeedTicket> cost = new Task<SpeedTicket>(ProcessInfraction);

            cost.Start();

            MessageBox.Show("Ticket Summary: " + cost.Result.Summary, "Traffic Tickets Management");
        }
    }

    public record struct SpeedTicket
    {
        public (int limit, int driving) Speed { get; set; }
        public bool  Paid { get; set; }
        public float SpeedDifference => Speed.driving - Speed.limit;
        public float Amount
        {
            get
            {
                if (SpeedDifference < 10)
                    return 0;
                else if (SpeedDifference < 20)
                    return 75;
                else if (SpeedDifference < 35)
                    return 100;
                else if (SpeedDifference < 50)
                    return 150;
                else
                    return 225;
            }
        }

        public string Summary
        {
            get
            {
                string msg = "The infraction has not yet been paid.";

                if (Paid == true)
                    msg = "The ticket has already been paid.";

                return $"The speed on the road is {Speed.limit} mph. " +
                       $"The vehicle was driven at {Speed.driving} mph, " +
                       $"which is a difference of {SpeedDifference} mph. " +
                       $"Consequently, the driver was issued a ticket for ${Amount}."
                       + Environment.NewLine + Environment.NewLine + msg; }
        }
    }
}

This would produce:

The Value of a Functional Task

In the above example, we used our own class for the task. As we know already, the .NET Framework provides a rich collection of classes and structures. If one of those classes fulfills your needs, you can use it. Simply follow the same steps as above: Create a function that returns an object of the class, create and initialize a Task<> object, start the task, and finally access the Task<>.Result property of that task. Remember that the Task<>.Result property gives you access to the public property of the class on which the task is performed. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        Form CreateDialogBox()
        {
            Form frm = new Form();

            frm.FormBorderStyle = FormBorderStyle.FixedDialog;
            frm.MaximizeBox = false;
            frm.MinimizeBox = false;
            frm.StartPosition = FormStartPosition.CenterScreen;
            frm.Size = new System.Drawing.Size(620, 340);
            frm.Text = "Traffic Tickets Management";

            return frm;
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<Form> dlgBox = new Task<Form>(CreateDialogBox);

            dlgBox.Start();

            dlgBox.Result.ShowDialog();
        }
    }
}

This would produce:

Introducing Tasks and Classes

In the above examples of tasks that produce an object, we created a function that produced the desired object. As an alternative and as we saw in the previous lesson, you can either nest the function where it will be needed or you can create it as a delegate or a lambda expression. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<Form> dlgBox = new Task<Form>(() =>
            {
                Form frm = new Form();
                
                frm.FormBorderStyle = FormBorderStyle.FixedDialog;
                frm.MaximizeBox = false;
                frm.MinimizeBox = false;
                frm.StartPosition = FormStartPosition.CenterScreen;
                frm.Size = new System.Drawing.Size(620, 340);
                frm.Text = "Traffic Tickets Management";
                
                return frm;
            });

            dlgBox.Start();

            dlgBox.Result.ShowDialog();
        }
    }
}

In the above two examples, we used regular classes. Indeed, you can use an abstract class, a sealed class, or a structure. In fact, you can use an interface. In this case, specify the parameter type as the interface. Here is an example:

interface ITrafficTicket
{
}

class Exercise : Form
{
    void Specify()
    {
        Task<ITrafficTicket> applied = new Task<ITrafficTicket>(...);
    }
}

If you create a function or method that returns an interface, of course, make sure it returns a valid object. As you should know already, in the function or method, you can create an object using a class that implements the interface. Here is an example:

public Exercise
{
    ITrafficTicket CreateTicket()
    {
        ITrafficTicket law = new PoliceStop();

        return law;
    }

    void MakeItHappen()
    {
        Task<ITrafficTicket> applied = new Task<ITrafficTicket>(CreateTicket);
    }
}
    
interface ITrafficTicket
{
}

// A class that implements the interface
class PoliceStop : ITrafficTicket
{
}

Other than that, you can start the task and get its result, which would give you access to the properties of the interface. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        ITrafficTicket CreateTicket()
        {
            double pay  = 85;
            bool   done = false;

            ITrafficTicket law = new PoliceStop(pay, done);

            return law;
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<ITrafficTicket> applied = new Task<ITrafficTicket>(CreateTicket);

            applied.Start();

            MessageBox.Show("Ticket Summary: " + applied.Result.Summary, "Traffic Tickets Management");
        }
    }
    
    public interface ITrafficTicket
    {
        double Amount  { get; }
        string Summary { get; }
        bool   Paid    { get; set; }
    }

    public record struct PoliceStop : ITrafficTicket
    {
        public PoliceStop(double amount, bool status)
        {
            Amount = amount;
            Paid   = status;
        }

        public double Amount  { get; }
        public bool   Paid    { get; set; }
        public string Summary
        { 
            get
            {
                string msg = "The infraction has not yet been paid.";

                if (Paid == true)
                    msg = "The ticket has already been paid.";

                return "The vehicle was stopped because it kept moving on the road " +
                       "shoulder. The driver didn't seem to be in control. Upon " +
                       "arrest, the driver was incoherent to every question asked." +
                       Environment.NewLine + Environment.NewLine +
                       $"A ticket in the amount of ${Amount} was issued to the driver."
                       + Environment.NewLine + Environment.NewLine + msg;
                ;
            }
        }
    }
}

This would produce:

A Task that Produces an Object

Passing a Task as Argument

As you may know by now, a task is an object created from the Task<> class. This makes it possible for you to create a function or method that uses an object of the Task<> class as argument. In the body of the function, you can ignore or use the argument. When calling the function, make sure you pass an argument that is a Task<> type. Here is an example:

namespace GeneralWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        SpeedTicket ProcessInfraction()
        {
            return new SpeedTicket()
            {
                Speed = (55, 82),
                Paid = false
            };
        }

        void Provide(Task<SpeedTicket> impress)
        {
            impress.Start();

            MessageBox.Show("Ticket Summary: " + impress.Result.Summary, "Traffic Tickets Management");
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<SpeedTicket> cost = new Task<SpeedTicket>(ProcessInfraction);

            Provide(cost);
        }
    }

    public record struct SpeedTicket
    {
        public (int limit, int driving) Speed { get; set; }
        public bool Paid { get; set; }
        public float SpeedDifference => Speed.driving - Speed.limit;
        public float Amount
        {
            get
            {
                if (SpeedDifference < 10)
                    return 0;
                else if (SpeedDifference < 20)
                    return 75;
                else if (SpeedDifference < 35)
                    return 100;
                else if (SpeedDifference < 50)
                    return 150;
                else
                    return 225;
            }
        }

        public string Summary
        {
            get
            {
                string msg = "The infraction has not yet been paid.";

                if (Paid == true)
                    msg = "The ticket has already been paid.";

                return $"The speed on the road is {Speed.limit} mph. " +
                       $"The vehicle was driven at {Speed.driving} mph, " +
                       $"which is a difference of {SpeedDifference} mph. " +
                       $"Consequently, the driver was issued a ticket for ${Amount}."
                       + Environment.NewLine + Environment.NewLine + msg;
            }
        }
    }
}

This would produce:

A Task that Produces an Object

In the above example, we used a Task<> object that produces an object of a class type. In the same way, you can pass a Task<>that acts on an interface.

Returning an Objective Task

You can create a function or method that returns a task. Once again, you can make the task produce an object. You can use your own class. When creating the function, specify its return type as Task<> with a class as the parameter type. In the body of the function, make sure it returns an appropriate task. When calling the function, you can assign the call to a Task<> object. Here is an example:

namespace GeneralizedWork
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        SpeedTicket ProcessInfraction()
        {
            return new SpeedTicket()
            {
                Speed = (35, 76),
                Paid  = false
            };
        }

        void Provide(Task<SpeedTicket> impress)
        {
            impress.Start();

            MessageBox.Show("Ticket Summary: " + impress.Result.Summary, "Traffic Tickets Management");
        }

        Task<SpeedTicket> Produce()
        {
            Task<SpeedTicket> done = new Task<SpeedTicket>(ProcessInfraction);

            return done;
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<SpeedTicket> cost = Produce();

            Provide(cost);
        }
    }

    public record struct SpeedTicket
    {
        public (int limit, int driving) Speed { get; set; }
        public bool  Paid { get; set; }
        public float SpeedDifference => Speed.driving - Speed.limit;
        public float Amount
        {
            get
            {
                if (SpeedDifference < 10)
                    return 0;
                else if (SpeedDifference < 20)
                    return 75;
                else if (SpeedDifference < 35)
                    return 100;
                else if (SpeedDifference < 50)
                    return 150;
                else
                    return 225;
            }
        }

        public string Summary
        {
            get
            {
                string msg = "The infraction has not yet been paid.";

                if (Paid == true)
                    msg = "The ticket has already been paid.";

            return $"The speed on the road is {Speed.limit} mph. " +
                   $"The vehicle was driven at {Speed.driving} mph, " +
                   $"which is a difference of {SpeedDifference} mph. " +
                   $"Consequently, the driver was issued a ticket for ${Amount}."
                   + Environment.NewLine + Environment.NewLine + msg; }
        }
    }
}

This would produce:

A Task that Produces an Object

Returning an Interfaced Task

As done for a regular class, you can create a function or method that returns a task that produces an interface. Of course, the parameter type of the Task<> must be an interface. Otherwise, the step is primarily the same as those of a class as seen above. Here is an example:

namespace TrafficTicketsManagement
{
    public partial class TrafficSystem : Form
    {
        public TrafficSystem()
        {
            InitializeComponent();
        }

        ITrafficTicket CreateTicket()
        {
            double pay  = 125;
            bool   done = true;

            ITrafficTicket law = new PoliceStop(pay, done);

            return law;
        }

        Task<ITrafficTicket> Adjust()
        {
            return new Task<ITrafficTicket>(CreateTicket);
        }

        private void btnTask_Click(object sender, EventArgs e)
        {
            Task<ITrafficTicket> applied = Adjust();

            applied.Start();

            MessageBox.Show("Ticket Summary: " + applied.Result.Summary, "Traffic Tickets Management");
        }
    }

    public interface ITrafficTicket
    {
        double Amount  { get; }
        string Summary { get; }
        bool   Paid    { get; set; }
    }

    public record struct PoliceStop : ITrafficTicket
    {
        public PoliceStop(double amount, bool status)
        {
            Amount = amount;
            Paid = status;
        }

        public double Amount { get; }
        public bool   Paid { get; set; }
        public string Summary
        {
            get
            {
                string msg = "The infraction has not yet been paid.";

                if (Paid == true)
                    msg = "The ticket has already been paid.";

                return "The vehicle was stopped because it kept moving on the road " +
                       "shoulder. The driver didn't seem to be in control. Upon " +
                       "arrest, the driver was incoherent to every question asked." +
                       Environment.NewLine + Environment.NewLine +
                       $"A ticket in the amount of ${Amount} was issued to the driver."
                       + Environment.NewLine + Environment.NewLine + msg;
                ;
            }
        }
    }
}

This would produce:

A Task that Produces an Object

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2014-2024, FunctionX Monday 05 July 2024, 11:17 Next