繁体   English   中英

如何为调用方法制定特定顺序?

[英]How to make a specific order for calling methods?

例如,我有一个带有接口的类,它的方法很少。 始终仅按类中的特定顺序调用方法的最佳方法是什么?

public class SomeClass
{
    void Start(ISomeInterface testClass)
    {
        testClass.Method1();
        testClass.Method2();
        testClass.Method3();
    }
}

public interface ISomeInterface
{
    void Method1();//should run 2nd

    void Method2();// 1st

    void Method3();// 3rd

}

看看模板方法设计模式

模板方法设计模式的目的是在操作中定义算法的框架,将某些步骤推迟到客户端子类。 模板方法允许子类重新定义算法的某些步骤,而无需更改算法的结构。

abstract class SomeClass : ISomeInterface
{
    public abstract void Method1();

    public abstract void Method2();

    public abstract void Method3();

    // The template method
    public void Start()
    {
        testClass.Method1();
        testClass.Method2();
        testClass.Method3();
    }
}

class ImplementationClass : SomeClass
{
    public override void Method1()
    {
        ...
    }

    public override void Method2()
    {
        ...
    }

    public override void Method3()
    {
        ...
    }
}

// Usage
var implementationClass = new ImplementationClass();
implementationClass.Start();

编写代码是正常的,这样方法才能按特定顺序运行。 但是在那种情况下,我们不想只公开所有方法,并希望调用者只是“知道”以一定顺序运行它们。 如果需要某些东西,那么我们必须以某种方式执行它。

如果接口的方法可以按任何顺序执行,但是在一种特定情况下,我们希望按特定顺序运行它们,那很简单。 我们只是按照我们想要的顺序进行操作:

    testClass.Method2();
    testClass.Method1();
    testClass.Method3();

如果方法必须始终按特定顺序执行,那么公开一个允许我们按任意顺序执行它们的接口就没有意义。 该接口应描述我们如何使用该类。 在这种情况下,这更有意义:

public interface IDoesSomething
{
    void DoSomething();
}

public class DoesSomething : IDoesSomething
{
    public void DoSomething()
    {
        DoAnotherThing();
        DoOneThing();
        SomethingElse();
    }

    private void DoOneThing(){}
    private void DoAnotherThing(){}
    private void SomethingElse(){}
}

现在,该接口告诉其他类如何与之交互,但是如何完成操作的细节(包括特定的步骤顺序)被封装(隐藏)在该类的实现中。

我们仍然在做同样的事情-将一个过程分成多个步骤-但是选择我们在课堂外公开的过程量。 通过使不可能的错误使用,我们使正确使用我们的类变得更加容易。

据我所知, Template method不是您想要的。 (除非您是那些不接受答案而使用答案的不愉快的人之一;)如果您想给用户一种自由的幻想,并以错误的方式惩罚用户,可以采用以下方法。

定义一个属性:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class OrderAttribute : Attribute
{
    public int Order { get; }
    public OrderAttribute(int order) => Order = order;
}

然后定义一个接口:

public interface IObeyOrder
{
    [Order(2)]
    [Order(4)]
    void Method1(); // should run 2nd or 4th

    [Order(1)]
    void Method2(); // 1st

    [Order(3)]
    void Method3(); // 3rd

    void Method4(); // order doesn't matter
}

并在一个类上实现它,首先在每个方法中调用CheckOrder()

public partial class ObeyOrder : IObeyOrder
{
    public void Method1()
    {
        CheckOrder();
        Console.WriteLine("Method1");
    }

    public void Method2()
    {
        CheckOrder();
        Console.WriteLine("Method2");
    }

    public void Method3()
    {
        CheckOrder();
        Console.WriteLine("Method3");
    }

    public void Method4()
    {
        CheckOrder();
        Console.WriteLine("Method4");
    }

    public void Method5() // non-interface
    {
        CheckOrder();
        Console.WriteLine("Method5");
    }
}

其中CheckOrder()是:

public partial class ObeyOrder : IObeyOrder
{
    private static readonly Dictionary<string, int[]> orderedMethods = OrderHelper<IObeyOrder>.OrderedMethods;

    private readonly Queue<int> orders = new Queue<int>(orderedMethods.Values.SelectMany(i => i).OrderBy(i => i));

    private void CheckOrder([CallerMemberName] string methodName = "")
    {
        if (!orderedMethods.TryGetValue(methodName, out var methodOrders))
            return;

        var order = orders.Peek();
        if (!methodOrders.Contains(order))
            throw new Exception($"Wrong method call order. Method '{methodName}' with orders [{string.Join(", ", methodOrders)}]. Expected order {order}.");

        orders.Enqueue(orders.Dequeue());
    }
}

当然,您可以在非局部类中做到这一点。

public static class OrderHelper<T>
{
    public static Dictionary<string, int[]> OrderedMethods { get; } = typeof(T)
        .GetMethods()
        .Select(method => new
        {
            Method = method.Name,
            Orders = method.GetCustomAttributes(typeof(OrderAttribute), false)
                           .Cast<OrderAttribute>()
                           .Select(attribute => attribute.Order)
                           .ToArray()
        })
        .Where(method => method.Orders.Length > 0)
        .ToDictionary(method => method.Method, method => method.Orders);
}

用法:

var obeyOrder = new ObeyOrder();
obeyOrder.Method2(); // should go 1st
obeyOrder.Method4(); // can go whenever, since there is no order attribute
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method5(); // can go whenever, since it's non-interface
obeyOrder.Method3(); // should go 3rd
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method2(); // should go 1st (after the last had been already called)

工作正常,但是

var obeyOrder = new ObeyOrder();
obeyOrder.Method2(); // should go 1st
obeyOrder.Method4(); // can go whenever, since there is no order attribute
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method5(); // can go whenever, since it's non-interface
obeyOrder.Method3(); // should go 3rd
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method2(); // should go 1st (after the last had been already called)
obeyOrder.Method2(); // should throw since the 2nd (obeyOrder.Method1()) is expected

抛出

方法调用顺序错误。 带有命令[1]的方法“ Method2”。 预期订单2。

首先,我认为您混淆了一些概念,一个类实现了一个接口,您不能拥有一个接口类。 通过实现接口所做的工作是确保接口的使用者类必须在其代码中实现该方法签名。

其次,如果方法位于接口中,则无法按一定顺序执行方法,这是因为接口方法(不是每个方法的代码本身,接口上没有任何逻辑)。 可能您在这里查找的是类(可以抽象地确定(尽管不确定为什么需要一个接口)),并且您可以将这3个方法作为其私有成员使用,并可以使用执行这3个方法的公共方法。 像这样:

public class Example
{

    private void MethodA()
    {
        //logic from methodA
    }

    private void MethodB()
    {
        //logic from methodB
    }

    private void MethodC()
    {
        //logic from methodC
    }

    public void MethodA()
    {
        MethodB();
        MethodA();
        MethodC();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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