Как я могу связать методы в зависимости от ввода пользователя? - PullRequest
0 голосов
/ 20 апреля 2020

У меня есть метод, который я использую для обработки изображений (поворот, фильтрация, изменение размера и т. Д. c). Это выглядит так:

 public Image ProcessImage(Image image, Func<ImageFactory, ImageFactory> process)
    {
        using (var imageFactory = new ImageFactory(preserveExifData: true))
        {
            using (var imageStream = new MemoryStream())
            {
                var loadResult = imageFactory.Load(image);
                var processResult = process(loadResult);
                processResult.Save(imageStream);
                return Image.FromStream(imageStream);
            }
        }
    }

Так как я использую Fun c <> таким образом, я могу просто вызвать нужный метод редактирования так:

_imageEditor.ProcessImage(_image, im => im.Resize(size))

Какой означает, что я могу связывать методы, такие как:

_imageEditor.ProcessImage(_image, im => im.Resize(size).Rotate(54).Flip(true))

Мой вопрос, как я могу связать эти методы в зависимости от использования ввода? Поэтому, если мой пользователь хочет одновременно повернуть и изменить размер, я мог бы просто добавить методы .Resize и .Rotate (которые, кстати, принимают разные параметры).

Конечно, я мог бы использовать кучу bools, но если бы у меня было множество методов редактирования, это стало бы невозможно, очень и очень уродливо использовать.

Есть ли способ добавить методы в эту цепочку, и если да, то как бы вы go сказали об этом?

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

Самый простой способ связать методы вместе - это использовать метод Aggregate LINQ.

Вам просто нужно изменить сигнатуру метода на Image ProcessImage(Image image, params Func<ImageFactory, ImageFactory>[] processes), и тогда вы можете сделать это:

public Image ProcessImage(Image image, params Func<ImageFactory, ImageFactory>[] processes)
{
    using (var imageFactory = new ImageFactory(preserveExifData: true))
    {
        using (var imageStream = new MemoryStream())
        {
            var loadResult = imageFactory.Load(imageStream);
            var processResult = processes.Aggregate(loadResult, (r, p) => p(r));
            processResult.Save(imageStream);
            return Image.FromStream(imageStream);
        }
    }
}

Теперь вам нужно только построить свой Func<ImageFactory, ImageFactory>[] processes из выбранных пользователем.

0 голосов
/ 20 апреля 2020

Ваш вопрос не ясен на 100%. Вы оставили много деталей. Но, похоже, вы спрашиваете, как последовательно вызывать ваши методы, передавать результат от одного вызова к другому, или как использовать входы, созданные во время выполнения, для создания этого списка вызовов, или и то, и другое.

Без Минимального воспроизводимого примера у меня нет ни хорошего способа воспроизвести ваш сценарий, ни предоставить решение, которое определило бы c для этого сценария. Но вот демонстрация основных c техник, которые вы могли бы использовать для достижения sh этих целей:

class Program
{
    private static readonly Dictionary<string, Func<int[], Func<A, A>>> _parser =
        new Dictionary<string, Func<int[], Func<A, A>>>()
    {
        { "Init", MakeInitFunc },
        { "Add", MakeAddFunc },
        { "Multiply", MakeMultiplyFunc },
        { "Negate", MakeNegateFunc },
    };

    static void Main(string[] args)
    {
        (string, int[])[] ops =
        {
            ("Init", new [] { 17 }),
            ("Add", new [] { 5 }),
            ("Multiply", new [] { 2 }),
            ("Negate", new int[0]),
        };

        Console.WriteLine(Chain(new A(), OpsToDelegates(ops)).Value);
        Console.WriteLine(Chain(new A(), OpsToDelegates(ops).Reverse()).Value);
    }

    private static IEnumerable<Func<A,A>> OpsToDelegates(IEnumerable<(string Name, int[] Args)> ops)
    {
        foreach (var op in ops)
        {
            yield return _parser[op.Name](op.Args);
        }
    }

    private static A Chain(A a, IEnumerable<Func<A, A>> ops)
    {
        foreach (Func<A, A> op in ops)
        {
            a = op(a);
        }

        return a;
    }

    private static Func<A, A> MakeInitFunc(int[] args)
    {
        return a => a.Init(args[0]);
    }

    private static Func<A, A> MakeAddFunc(int[] args)
    {
        return a => a.Add(args[0]);
    }

    private static Func<A, A> MakeMultiplyFunc(int[] args)
    {
        return a => a.Add(args[0]);
    }

    private static Func<A, A> MakeNegateFunc(int[] args)
    {
        return a => a.Negate();
    }
}

class A
{
    public int Value { get; private set; }

    public A Init(int value) { Value = value; return this; }
    public A Add(int value) { Value += value; return this; }
    public A Multiply(int value) { Value *= value; return this; }
    public A Negate() { Value *= -1; return this; }
}

Существует два ключевых элемента выше:

  1. Учитывая последовательность из Func<A, A> делегатов, можно связать их вместе с простым l oop. О том, как это можно сделать, см. В методе Chain().
  2. Учитывая некоторый пользовательский ввод, можно преобразовать его в последовательность Func<A, A> делегатов. На самом деле существует очень широкий спектр возможных способов решения этой конкретной проблемы. Мой пример показывает очень простую технику, использующую словарь, который отображает входные значения string для вспомогательных методов, которые выполняют фактическую работу по генерации элементов последовательности. См. OpsToDelegates().

Объединяя эти два в эту простую программу, вы можете увидеть, как вы можете начать с простого списка имен операций и параметров для их применения, и превратить это в функциональная последовательность операций, фактически примененная к объекту.

Я надеюсь, что вы можете взять эти общие идеи и применить их к вашему конкретному сценарию.

...