在回调上实现可扩展性?

时间:2017-08-14 19:01:40

标签: c# design-patterns

目前,我正在开发一个API,开发人员可以订阅它以了解更新。

所以现在我正在实现一个接口IResult,这样我就可以在回调结果中发送不同的参数。现在的问题是,如果在将来,我想添加一个新的回调,我必须在方法中添加一个参数,开发人员还需要更改他们的方法调用。对此有一个很好的解决方案吗?

public interface IResult
{
    int i { get; set; }
}

public class ConcreteResult : IResult
{
    public int i
    {
        get;set;
    }
}


public class MyAPI
{
    public delegate void MyAPIDelegate(IResult result);

    public void StartService(MyAPIDelegate callback, MyAPIDelegate callback2)
    {
        //step 1
        int i = 0;
        ConcreteResult result1 = new ConcreteResult();
        result1.i = i;
        callback(result1);
        //step 2
        i += 1;
        ConcreteResult result2 = new ConcreteResult();
        result2.i = i;
        callback2(result2);
        //potentially added in the future
        //i += 1;
        //callback3();
    }

    public void main()
    {
        //developers use my API
        StartService(developerCallback, developerCallback2);
    }
    private void developerCallback(IResult result)
    {
        Console.WriteLine(result.i);
    }
    private void developerCallback2(IResult result)
    {
        Console.WriteLine(result.i);
    }
}

2 个答案:

答案 0 :(得分:1)

奇怪的是每个人都在推荐活动,但没有人展示一个例子。我咬了。

根据命名惯例来判断我猜你来自Java领域。 (C#方法一般是PascalCase)。 C#有events,这使得这样的事情变得更加简单。我建议你研究它们,因为它们在C#代码中很常见。

您所要做的就是在您的类上定义一个公共事件,并让该类在必要时调用该事件。 (执行?.因为未订阅的事件非常奇怪。)

然后从使用消费类中,使用+=订阅处理程序。

这使您可以在将来添加新活动,而无需消费者担心。

public class MyAPI
{
    public event Action<IResult> Callback1;

    public event Action<IResult> Callback2;

    public void StartService()
    {
        //step 1
        int i = 0;
        ConcreteResult result1 = new ConcreteResult();
        result1.i = i;
        Callback1?.Invoke(result1);
        //step 2
        i += 1;
        ConcreteResult result2 = new ConcreteResult();
        result2.i = i;
        Callback2?.Invoke(result2);
        //potentially added in the future
        //i += 1;
        //callback3();
    }
}

public static class Program { 
    public static void Main()
    {
        //developers use my API
        var api = new MyAPI();
        api.Callback1 += DeveloperCallback;
        api.Callback2 += DeveloperCallback2;

        api.StartService();
    }
    private static void DeveloperCallback(IResult result)
    {
        Console.WriteLine(result.i);
    }
    private static void DeveloperCallback2(IResult result)
    {
        Console.WriteLine(result.i);
    }
}

对于简单的事件处理程序,您可以内联订阅:

api.Callback1 += result =>
    {
        Console.WriteLine(result.i);
    };

甚至更简单的单行:

api.Callback1 += result => Console.WriteLine(result.i);

既然你问过,另一种选择比简单事件更重一些,但最终更强大的是Reactive Extensions。如果你想使用它们,那么你可以编写如下代码:

using System.Reactive.Subjects;

public class MyAPI
{
    private readonly Subject<IResult> callback1 = new Subject<IResult>();

    private readonly Subject<IResult> callback2 = new Subject<IResult>();

    public void StartService()
    {
        //step 1
        int i = 0;
        ConcreteResult result1 = new ConcreteResult();
        result1.i = i;
        callback1.OnNext(result1);
        //step 2
        i += 1;
        ConcreteResult result2 = new ConcreteResult();
        result2.i = i;
        callback2.OnNext(result2);
    }

    public IObservable<IResult> Callback1 => this.callback1;

    public IObservable<IResult> Callback2 => this.callback2;
}

public static class Program
{
    public static void Main()
    {
        var api = new MyAPI();

        // Subscribing returns a disposable subscription, and disposing it unsubscribes.
        // That means you can use lambda syntax and still unsubscribe later
        IDisposable subscription =
            api.Callback1.Subscribe(result => Console.WriteLine(result.i)); 
        api.StartService(); // Writes result.

        // Once disposed, event is no longer called
        subscription.Dispose();
        api.StartService(); // Doesn't write result.

        // Since IDisposable is a special thing that can be scoped to using blocks in C#, you can do the following:
        using (api.Callback1.Subscribe(result => Console.WriteLine(result.i)))
        {
            api.StartService(); // Writes result
        }
        api.StartService(); // Doesn't write result

    }
}

答案 1 :(得分:0)

我强烈建议您使用@Vikhram建议的事件,但这是您的示例,修改为使用您请求的类。

请注意,在调用函数时我没有指定Callback3。 API会在调用它时使用.?,而不仅仅是.,因此如果开发人员没有通过NullReferenceException,它就不会导致MyCallbackInfo

当您添加更多回调时,只需向public interface IResult {... } public class ConcreteResult : IResult {...} public class MyStartServiceCallbackInfo { public MyAPI.MyAPIDelegate Callback1 { get; set; } public MyAPI.MyAPIDelegate Callback2 { get; set; } public MyAPI.MyAPIDelegate Callback3 { get; set; } } public class MyAPI { public delegate void MyAPIDelegate(IResult result); public void StartService(MyStartServiceCallbackInfo callbacks) { ... callbacks?.Callback1(result1); ... callbacks?.Callback2(result2); ... callbacks?.Callback3(result3); } public void main() { StartService(new MyCallbackInfo() { Callback1 = developerCallback, Callback2 = developerCallback2, }); } private void developerCallback(IResult result) { Console.WriteLine(result.i); } private void developerCallback2(IResult result) { Console.WriteLine(result.i); } } 添加其他属性,并调用它们与现有属性相同。

""