改善代码重用(Python风格的c#装饰器?)

时间:2018-06-29 10:50:36

标签: c# python dry

我正在从python转到c#,而我想念的一件事是python风格的装饰器。如果我在许多功能(验证检查等)的顶部重复了代码,则可以创建一个装饰器来执行此操作。

我已经看到有一些c#装饰器,但是它们看起来在类级别上工作更多。尽管我对他们有些困惑。

无论如何-您将如何改善此功能中的代码重用/ DRY?该功能中的所有内容都是通用的,除了标记的两个位置。它的回调驱动了向服务器的Tcp请求,并带有一个阻止多个并发请求的块(检查空闲状态)。

    public void MyFunction(string apples, Action<TcpRequest> onSuccess=null, Action<TcpRequest> onError=null)
    {
        // Throw exception if already busy with an operation
        if (!state.Has(State.Idle)) { throw new OperationInProgress(); }

        // Define callback action
        Action<TcpRequest> callback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(State.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                /**
                 * Unique code here
                 */
            }
            // Request failed. Call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Remove idle state
        state.Remove(State.Idle);

        /**
         * Unique code here, that will later trigger the callback
         */
    }

编辑:我并不是真的将其视为代码审查任务,但是现在我可以看到了。这是整个课堂,展示了状态/变量如何相互作用。 Server类负责处理我们(客户端)与网络服务器之间的交互,以处理游戏登录以及创建/加入比赛。

我并没有固定任何特定的结构,但是在某个时候,我想将UI按钮连接到简单的功能,例如Server.Login()Server.JoinMatch(),而无需产生混乱的类。

public class Server
{
    #region Fields
    public string playerName { get; private set; }
    public string playerID { get; private set; }
    public string playerToken { get; private set; }
    public string currentMatchID { get; private set; }
    private Patterns.State<ServerState> state = new Patterns.State<ServerState>();
    #endregion

    public Server()
    {
        state.Add(ServerState.Idle);
    }

    public void Login(string playerName, Action<TcpRequest> onSuccess = null, Action<TcpRequest> onError = null)
    {
        // Throw exception already busy with an operation
        if (!state.Has(ServerState.Idle)) { throw new OperationInProgress(); }

        // Define login callback action
        Action<TcpRequest> loginCallback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(ServerState.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                // Store player data in class
                playerName = (string)request.requestJson["player_name"];
                playerID = (string)request.responseJson["player_id"];
                playerToken = (string)request.responseJson["player_token"];

                // Add the logged in state
                state.Add(ServerState.LoggedIn);

                // Call the onSuccess callback if provided
                onSuccess?.Invoke(request);
            }
            // Login failed, call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Remove idle state
        state.Remove(ServerState.Idle);

        // Perform request
        Request("login", callback: loginCallback, requestJson: new Dictionary<string, object> { { "player_name", playerName }, { "client_version", "test1" } });
    }

    public void CreateMatch(string matchName, Action<TcpRequest> onSuccess = null, Action<TcpRequest> onError = null)
    {
        // Throw exception already busy with an operation
        if (!state.Has(ServerState.Idle)) { throw new OperationInProgress(); }

        // Define callback action
        Action<TcpRequest> callback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(ServerState.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                // Add the inLobby state
                state.Add(ServerState.InLobby);

                // Call the onSuccess callback if provided
                onSuccess?.Invoke(request);
            }
            // Request failed. Call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Remove idle state
        state.Remove(ServerState.Idle);

        // Perform request
        AuthenticatedRequest("match/create", callback: callback, requestJson: new Dictionary<string, object> { { "match_name", matchName } });
    }

    public void JoinMatch(string matchID, Action<TcpRequest> onSuccess = null, Action<TcpRequest> onError = null)
    {
        // Throw exception already busy with an operation
        if (!state.Has(ServerState.Idle)) { throw new OperationInProgress(); }

        // Define callback action
        Action<TcpRequest> callback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(ServerState.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                // Add the inLobby state
                state.Add(ServerState.InLobby);

                // Set currentMatchID in class
                currentMatchID = (string)request.responseJson["match_id"];

                // Call the onSuccess callback if provided
                onSuccess?.Invoke(request);
            }
            // Request failed. Call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Perform request
        AuthenticatedRequest("match/join", callback: callback, requestJson: new Dictionary<string, object> { { "match_id", matchID } });
    }

    private void Request(string resource, Action<TcpRequest> callback = null, Dictionary<string, object> requestJson = null)
    {
        // Start async request, invoke callback when done
    }

    private void AuthenticatedRequest(string resource, Action<TcpRequest> callback = null, Dictionary<string, object> requestJson = null)
    {
        // Add login auth data into the requestJson dict or throw exception if we aren't logged in
        // Call Request()
    }
}

1 个答案:

答案 0 :(得分:1)

取决于这两个唯一的代码,必须始终成对使用,否则我会选择其他方法。

如果要强制使用“配对用法”,则可以使用抽象类:

public abstract class MyClass
{
    public void MyFunction(string apples, Action<TcpRequest> onSuccess=null, Action<TcpRequest> onError=null)
    {
        // Throw exception if already busy with an operation
        if (!state.Has(State.Idle)) { throw new OperationInProgress(); }

        // Define callback action
        Action<TcpRequest> callback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(State.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                SomethingUnique1();
            }
            // Request failed. Call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Remove idle state
        state.Remove(State.Idle);

        SomethingUnique2(callback);
    }
    protected abstract void SomethingUnique1();
    protected abstract void SomethingUnique2(Action<TcpRequest> callback);
}

然后根据需要实现尽可能多的子类:

public sealed class MyClassVariant1 : MyClass
{
    protected override SomethingUnique1() { /*...*/ }
    protected override SomethingUnique2(Action<TcpRequest> callback) { /*...*/ }
}

public sealed class MyClassVariant2 : MyClass
{
    protected override SomethingUnique1() { /*...*/ }
    protected override SomethingUnique2(Action<TcpRequest> callback) { /*...*/ }
}

如果由于一个“唯一的东西1”可能与许多“唯一的东西2”同时使用而不能强制使用配对,我会鼓励采用装饰性的方法:

public sealed class MyClass
{
    private readonly Action somethingUnique1;
    private readonly Action<TcpRequest> somethingUnique2;
    public MyClass(Action somethingUnique1, Action<TcpRequest> somethingUnique2)
    {
        this.somethingUnique1 = somethinUnique1;
        this.somethinUnique2 = somethingUnique2;
    }
    public void MyFunction(string apples, Action<TcpRequest> onSuccess=null, Action<TcpRequest> onError=null)
    {
        // Throw exception if already busy with an operation
        if (!state.Has(State.Idle)) { throw new OperationInProgress(); }

        // Define callback action
        Action<TcpRequest> callback = delegate (TcpRequest request)
        {
            // Add idle state back in
            state.Add(State.Idle);

            // Check if the request succeeded
            if (request.OK)
            {
                somethingUnique1();
            }
            // Request failed. Call the onError callback if provided
            else { onError?.Invoke(request); }
        };

        // Remove idle state
        state.Remove(State.Idle);

        somethingUnique2(callback);
    }
}

然后

var variant1 = new MyClass(() => { /* ... */ }, (TcpRequest r) => { /* ... */ }); 
var variant2 = new MyClass(() => { /* ... */ }, (TcpRequest r) => { /* ... */ }); 

这里的方法更容易组合,因此限制更少。