Передача предиката в качестве параметра c # - PullRequest
0 голосов
/ 15 марта 2019

Недавно я провел оценку в компании, в которой был случай, когда они хотели установить предикат в качестве входного параметра для метода.У меня мало опыта или нет, я исследовал его самостоятельно.Код выглядит так:

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();
    }
}

Мой вопрос: чего ожидает функция Egg и почему?

Я уже видел этот ответ и этотответ и этот ответ но все же он не имеет никакого смысла.Это академично, но я действительно хочу понять.

Ответы [ 2 ]

6 голосов
/ 15 марта 2019

public Egg(Func<IBird> createBird) - это не функция, это конструктор класса Egg.Так как класс Egg должен Hatch птиц, он должен создавать птиц.Func<IBird> является делегатом, т. Е. Значением, представляющим ссылку на метод.В данном конкретном случае он представляет собой фабричный метод .Предикатом будет метод или делегат, возвращающий логическое значение.Через этот параметр вы можете передать любой метод, создающий IBird s.Поскольку интерфейс IBird не определяет явную реализацию птицы, вы можете инициализировать Egg различными методами, создавая разные типы птиц.Некоторым требуются параметры конструктора, некоторые нет.

Вы бы реализовали Egg следующим образом

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 "()".
    }
}

Теперь метод Hatch может создавать птиц, не зная, как и какиетип птицы для создания через посредника делегата _createBird.

Как бы вы создали яйцо?Ну, во-первых, вам нужна реализация птицы, например:

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

Затем вам нужен метод создания и возврата IBird.Например:

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

Затем вы можете создать яйцо с

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

. Обязательно передайте метод без списка параметров, то есть без скобок, потому что вы не хотите вызыватьCreateBlackBird метод на этом этапе, вы хотите передать его конструктору, где он хранится в закрытом поле _createBird для последующего использования.

Лямбда-выражение создает анонимный делегат на лету:

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

() => new BlackBird() является лямбда-выражением.Это эквивалентно методу CreateBlackBird.Тип возвращаемого значения не указан и выводится из типа параметра конструктора Egg.У него нет имени.От заголовка метода остаются только фигурные скобки.=> заменяет ключевое слово return.

После реализации дополнительного класса птицы с цветом в качестве параметра конструктора вы можете написать

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

См. Также:

0 голосов
/ 30 мая 2019

Спасибо за этот пример.Вы можете реализовать шаблон синглтона, чтобы птица могла выводиться только один раз, изменив класс Egg следующим образом:

    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;
    }
}
...