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

时间:2019-05-13 14:59:24

标签: c# class interface

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

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

}

4 个答案:

答案 0 :(得分:3)

看看Template Method Design Pattern

  

模板方法设计模式的目的是在操作中定义算法的框架,从而延迟一些操作。   客户子类的步骤。模板方法可让子类重新定义   算法的某些步骤而无需更改算法的   结构。

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

答案 1 :(得分:2)

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

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

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

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

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

答案 2 :(得分:1)

据我所知,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 :(得分:0)

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

第二,如果方法位于接口中,则无法按一定顺序执行方法,这是因为接口方法(不是每个方法的代码本身,接口上没有任何逻辑)。可能您在这里查找的是类(可以抽象地确定(尽管不确定为什么需要一个接口)),并且您可以将这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();
    }
}