简体   繁体   中英

Passing a predicate as a parameter c#

I recently did an assessment from a company that had a case where they wanted to set up a predicate as an input parameter to a method. Have little to no experience with this I've been researching it on my own. The code looks like:

using System;

public interface IBird
{
    Egg Lay();
}

public class Chicken : IBird
{
    public Chicken()
    {
    }

    public void EggLay()
    {
    }

    public Egg Lay()
    {
        return new Egg();
    }
}

public class Egg
{
    public Egg(Func<IBird> createBird)
    {
        throw new NotImplementedException("Waiting to be implemented.");
    }

    public IBird Hatch()
    {
        throw new NotImplementedException("Waiting to be implemented.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
//      var chicken1 = new Chicken();
//      var egg = chicken1.Lay();
//      var childChicken = egg.Hatch();
    }
}

My question is what is the Egg function expecting and why?

I've already seen this answer and this answer and this answer but it's not making any sense still. It's academic at this point but I really want to understand.

public Egg(Func<IBird> createBird) is not a function, it's the constructor of the Egg class. Since the Egg class must Hatch birds, it needs to create birds. Func<IBird> is a delegate, ie, a value representing a reference to a method. In this specific case it is representing a factory method . A predicate would be a method or delegate returning a Boolean. Through this parameter you can pass any method creating IBird s. Since the IBird interface does not specify an explicit implementation of a bird, you could initialize Egg with different methods creating different bird types. Some requiring constructor parameters, some not.

You would implement Egg like this

public class Egg
{
    private readonly Func<IBird> _createBird;

    public Egg(Func<IBird> createBird)
    {
        _createBird = createBird; // No "()". createBird is not called, just assigned.
    }

    public IBird Hatch()
    {
        return _createBird(); // Here createBird is called, therefore the "()".
    }
}

Now, the Hatch method can create birds, without having the knowledge about how or which type of bird to create, through the intermediate of the _createBird delegate.

How would you create an egg? Well, first you need some bird implementation eg:

public class BlackBird : IBird
{
    ... your implementation goes here
}

Then you need a method creating and returning a IBird . Eg:

IBird CreateBlackBird()
{
    return new BlackBird();
}

You can then create an egg with

var egg = new Egg(CreateBlackBird); // No "()". CreateBlackBird is not called but referenced.
IBird newBird = egg.Hatch();

Make sure to pass the method without parameter list, ie without parentheses, because you don't want to call the CreateBlackBird method at this point, you want to pass it over to the constructor, where it is stored in the private field _createBird to be used later.

A lambda expression creates an anonymous delegate on the fly:

var egg = new Egg(() => new BlackBird());

() => new BlackBird() is a lambda expression. It is equivalent to the CreateBlackBird method. The return type is not specified and is inferred from the parameter type of the Egg constructor. It has no name. Only the parameter braces are remaining from the method header. => replaces the return keyword.

After having implemented an additional bird class with a color as constructor parameter, you can write

var egg = new Egg(() => new ColoredBird(Color.Blue));

See also:

Thanks for this example. You can implement the singleton pattern so that the bird can hatch only once by modifying the Egg class as following:

    public class Egg
{
    private readonly Func<IBird> _createBird;
    private IBird _Bird = null;

    public Egg(Func<IBird> createBird)
    {
        _createBird = createBird;
    }

    public IBird Hatch()
    {
        if (_Bird == null)
        {
            _Bird = _createBird();
        }

        return _Bird;
    }
}

You can implement the singleton pattern so that the bird can hatch only once by modifying the Egg class as following.

public interface IBird
    {
        Egg Lay();
    }
    public class Egg
    {
        private readonly Func<IBird> _createBird;
        private bool _hatched;

        public Egg(Func<IBird> createBird)
        {
            _createBird = createBird;
        }

        public IBird Hatch()
        {
            if (_hatched)
                Console.WriteLine("Egg are already hatched");
            _hatched = true;
            Console.WriteLine("Egg are hatched now;");
            Console.ReadKey();
            return _createBird();
        }
    }

    public class Chicken : IBird
    {
        public Egg Lay()
        {
            var egg = new Egg(() => new Chicken());
            return egg;
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var chicken1 = new Chicken();
            var egg = chicken1.Lay();
            var childChicken = egg.Hatch();
        }
    }

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