简体   繁体   中英

Using delegate action and predicate as a argument for a method in a class

I have an exercise about delegate and lambda and the exercise tell me to use Action as argument, which receives a Flight and a price as parameters and prints the information of the flight, if the of the flight is less than the given price. I try to defined is like this but it is wrong, is there other correct way?

public string FindFlightWithLessPrice(Action<Flight , double> action, Flight flight, double price)
        {
            string result = "";
            action = (flight, price) => { price > flight.Price ? result += flight.ToString() : result += "" };
            return result;
        }

This is FLight class:

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

namespace Assignment_4
{
    class Flight
    {
        private int id;
        public int Id
        {
            get { return id; }
            set { this.id = value; }
        }

        private string origin;
        public string Origin
        {
            get { return origin; }
            set { this.origin = value; }
        }

        private string destination;
        public string Destination
        {
            get { return destination; }
            set { this.destination = value; }
        }

        private string date;
        public string Date
        {
            get { return date; }
            set { this.date = value; }
        }

        private double price;
        public double Price
        {
            get { return price; }
            set { this.price = value; }
        }

        public Flight(int id, string origin, string destination, string date, double price)
        {
            this.id = id;
            this.origin = origin;
            this.destination = destination;
            this.date = date;
            this.price = price;
        }

        public string FindFlight(int searchId)
        {
            if (searchId == this.id)
            {
                return this.ToString();

            }
            return "";
        }
        public override string ToString()
        {
            string s = "Flight ID: " + id + "\nOrigin: " + origin + "\nDestination: " + destination +
                        "\nDate: " + date + "\nPrice: " + price + "$";
            return s;
        }
    }
}

This is AirlineCompany class:

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

namespace Assignment_4
{
    public delegate string FindFlightDelegate(int id);
    class AirlineCompany
    {
        private readonly string airlineName;
        public string AirlineName
        {
            get { return airlineName; }
        }
        public AirlineCompany(string airlineName)
        {
            this.airlineName = airlineName;
        }
        private List<Flight> flights = new List<Flight>();
        public Flight this[int index]
        {
            set
            {
                flights.Insert(index, value);
            }
            get
            {
                return flights[index];
            }
        }
        public int Count()
        {
            return flights.Count;
        }
        public string FindFLight(int flightId)
        {
            string result = "";
            foreach (Flight f in flights)
            {
                result += f.FindFlight(flightId);
            }
            return result;
        }

        public string ReturnFlightWithPrice(FindFlightDelegate findFlightDelegate, double price)
        {
            string result = "";
            foreach (Flight f in flights)
            {
                if (price < f.Price)
                {
                    result += findFlightDelegate(f.Id);
                }
            }
            return result;
        }
        public string FindFlightWithLessPrice(Action<Flight , double> action, Flight flight, double price)
        {
            string result = "";
            action = (flight, price) => { price > flight.Price ? result += flight.ToString() : result += "" };
            return result;
        }
    }
}

This is the whole exercise:

  1. Write an application which can be used to manage the flight information of an airline company. To do this define class AirlineCompany so that its attributes are a read only field called airline name and a collection of flight information. For this purpose define class Flight, whose attributes are; id, origin, destination, date and price. For class Flight define necessary properties and constructors and FindFlight() method which returns the flight information if the correct flight id is provided. Define in class AirlineCompany a method, which receives a delegate and a price as an argument and applies the delegate if the given price is less than the price of the flight. The delegate should allow calling different methods for returning different sets of data about the flight, like full flight data or only origin and destination.

  2. Define in class AirlineCompany a method, which receives as argument an Action, which receives a Flight and a price as parameters and prints the information of the flight, if the of the flight is less than the given price.

  3. Define in class AirlineCompany a method, which receives as argument a Predicate, which returns the first flight, whose price is greater than 250 eur. To test the predicate make sure that some flights have prices greater than 250 eur.

Define in class AirlineCompany a method, which receives a delegate and a price as an argument and applies the delegate if the given price is less than the price of the flight. The delegate should allow calling different methods for returning different sets of data about the flight, like full flight data or only origin and destination.

So, there are still some queries for me around this; it's not a sufficiently precise enough spec for anything I'd like to write code to in a non academic environment but I suppose the main aim is to teach you about delegates. I don't know how much you've covered but here's a quick recap:

This here is a definition for a class representing a person. It's really pared down, just to demo a point

public class Person{
  public string Name{get;set;}
  public int Age{get;set;}
}

It doesn't hold any data itself, it's just a specification - "there shall be a Person, and it shall have a string called Name and an int called Age"

When you actually make a Person it becomes more useful

var p = new Person{ Name="John", Age=20 };

This represents a particualar variation of a person. You could write a method:

void PrintPerson(Person p){
  Console.WriteLine(p.Name + " is "+ p.Age);
}

The method accepts a person so it's a way of passing person data around to code that will do something with it


You're probably familiar with all this/it's logical


I'm now going to copypaste all that above and change it to be about delegates. Whenever I change some word I'll make it bold:

This here is a definition for a delegate representing a method . It's really pared down, just to demo a point

public delegate bool TakesStringAndIntAndReturnsBool(string s, int i);

It doesn't hold any code itself, it's just a specification - "there shall be a method , and it shall * take a string and an int and return a bool "

When you actually make a method that obeys it it becomes more useful

bool IsCalledJohnAndIs20(string name, int age){ return name == "John" && age == 20; }

This represents a particualar variation of a method . You could write a code :

 TakesStringAndIntAndReturnsBool x = IsCalledJohnAndIs20;

The variable x accepts a method so it's a way of passing a method code around to code that will do something with it


In essence, that's what a delegate is; a way of passing code instructions around as if it were data. Your data has a specification: it looks like a person. Your delegate has a spec too. In order to be able to "store code in a variable" we only need to know 1) what inputs the code has and 2) what output it has. What the code does , we don't care. Just like what name and age the user types in, we don't care - we only care that a Person looks like that - it's a Person with a Name and an Age. For delegates, we want it to be "a method that takes a string and an int and returns a bool" - what it does, we don't care. So long as it looks right, we can use it

So your assignment asks for

Define in class AirlineCompany a method, which receives a delegate and a price as an argument and applies the delegate if the given price is less than the price of the flight

This seems like you should have a delegate:

delegate string TakesAFlightAndReturnsAString(Flight f);

It's a dumb name, I know, but realistically, we don't need a delegate to have anything else, because what they specify is really limited. Perhaps we should call it something better:

delegate string FlightFormatter(Flight f);

Remember we're only specifying the signature of a method here, no code. Suppose we really did have some methods:

the delegate should allow calling different methods for returning different sets of data about the flight, like full flight data or only origin and destination.

This bit is woolly; "returning different data". Really we'd want the spec to be better than take, but let's assume it's a string formatted message built from a flight. And let's make some methods:

like full flight data

public string FormatFullFlightData(Flight f){
  return $"Flight number {f.Id} on date {f.Date} from {f.Origin} to {f.Destination} costing ${f.Price}";
}

or only origin and destination.

public string FormatOnlyOriginAndDest(Flight f){
  return $"Flight from {f.Origin} to {f.Destination}";
}

You could now make a couple of variables of type FlightFormatter and stash these methods in them:

FlightFormatter f1 = FormatFullFlightData; 
FlightFormatter f2 = FormatOnlyOriginAndDest;

You could call them, passing in a flight:

Flight f = new Flight(1, "earth", "the moon", "1970-01-01", 999999999);

var s1 = f1(f);
var s2 = f2(f);

Now s1 has your full flight and s2 is a string of just your origin and destination

  1. Define in class AirlineCompany a method, which receives as argument an Action, which receives a Flight and a price as parameters and prints the information of the flight, if the of the flight is less than the given price

This too is poorly worded. There's a word missing and the comma seems in the wrong place. I think it is asking:

  • Make a method in AirlineCompany with two arguments
  • The first argument shall be an Action. The Action shall have one argument of type Flight.
  • The second argument shall be a price
  • The AirlineCompany will look through all its flights. If a flight is cheaper than the price passed as the second argument, invoke the passed-in Action on that flight

That's a slight tweak to what you have, ie change (or copy) this:

public string ReturnFlightWithPrice(FindFlightDelegate findFlightDelegate, double price)
{
    string result = "";
    foreach (Flight f in flights)
    {
        if (price < f.Price)
        {
            result += findFlightDelegate(f.Id);
        }
    }
    return result;
}

And have it as:

public void InvokeIfFlightCheaperThan(Action<Flight> toInvoke, double targetPrice)
{
    foreach (Flight f in flights)
    {
        if (f.Price < targetPrice)
        {
            toInvoke(f);
        }
    }
}

It means you can have a method:

void PrintFullFlightData(Flight f){
  Console.WriteLine($"Flight number {f.Id} on date {f.Date} from {f.Origin} to {f.Destination} costing ${f.Price}");
}

And you can ask the airline co to call it for you on all flights less than a billion dollars:

Action<Flight> x = PrintFullFlightData;

yourAirlineCo.InvokeIfFlightCheaperThan(x, 1000000000);

You can skip stashing the method in a temp variable:

yourAirlineCo.InvokeIfFlightCheaperThan(PrintFullFlightData, 1000000000);

You can even skip making a full on method at all, and make an inline one:

yourAirlineCo.InvokeIfFlightCheaperThan(
  f => Console.WriteLine($"Flight number {f.Id} on date {f.Date} from {f.Origin} to {f.Destination} costing ${f.Price}"),
  1000000000
);

In this latter part, the compiler has all the information it needs to write a method for you; it doesn't need to know the name, it knows the inputs and it knows there is no output. Literally the only thing that this lambda is, is the method body. As it's only a single line, it doesn't even need the curly braces

These are equivalent:

void PrintFullFlightData(Flight f){
  Console.WriteLine($"Flight number {f.Id} on date {f.Date} from {f.Origin} to {f.Destination} costing ${f.Price}");
}

f => Console.WriteLine($"Flight number {f.Id} on date {f.Date} from {f.Origin} to {f.Destination} costing ${f.Price}")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM