簡體   English   中英

改善代碼重用(Python風格的c#裝飾器?)

[英]Improve Code Reuse (Python-style decorator for c#?)

我正從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()
    }
}

取決於是否必須始終成對使用兩個唯一的代碼,否則我會選擇其他方法。

如果要強制使用“成對用法”,則可以使用抽象類:

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) => { /* ... */ }); 

在這里,該方法更具可組合性,因此限制較少。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM