1

私は C# でサーバーを作成しており、ログインしているすべてのユーザーを配列で処理するシングルトンがあります。その配列は、UserSession というクラスの配列です。

この UserSession クラスには、そのユーザーの着信パケットを処理する別のスレッドで実行されるメソッドがあります。

さて、このコードを考えてみましょう:

class UserSession
{
    public UserSession(TcpClient client)
    {
        var thread = new Thread(new ParameterizedThreadStart(HandleComm);
        thread.Start((object)client);
    }

    public void HandleComm(object tcpClient)
    {
        TcpClient client = (TcpClient)tcpClient;
        NetworkStream stream = client.GetStream();
        while(1 == 1)
        {
            byte[] buffer = new byte[4096];
            stream.Read(buffer, 0, buffer.Length);
            int testShort = Convert.ToInt32(buffer);
            switch((ActionEnum)testShort)
            {
                case ActionEnum.Something:
                    // here is my problem!
                    // do some more parsing of the message
                    this.DoSomething(SomethingStruct argument); // argument taken from the byte array
                    break;
                case ActionEnum.AnotherSomething:
                    // the same as before
                    break;
                // and this goes on and on
            }
        }
    }
}

そのクラスで 80 を超えるメソッドを繰り返さずに、これらすべての個別の列挙型を処理する最善の方法は何でしょうか? (ActionEnum は、現在のユーザーの特定のアクションを持つ列挙型です)。

私はそのバッファをシリアル化し、アイデアを得るためにそのコードを高速にしました。

4

2 に答える 2

2

だから:あなたが基本的にやろうとしていることは、数値コードをメソッドの呼び出しに変換することです。

この変換が自動的に行われるなど、取得できないものがあります。代わりにメソッドの名前を送信した場合は、リフレクションを使用してメソッドを検索し、それを呼び出すことができます。したがって、マッピングを確立するには、手作業を行う必要があります。

次に決定しなければならないのは、数値をメソッドにマッピングする最良の方法です。

1 つの方法は、現在使用している方法です。ここでは、ディスパッチ サイクル内でマッピングが行われています (番号のフェッチ、変換、呼び出し)。問題は、マッピング コードがディスパッチ コードを覆い隠すことです。また、かなりの量の定型コードが関係しています。

次のように、コマンド パターンとハッシュ マップの組み合わせを使用できます。

セットアップ中:

  1. コマンド用の共通インターフェイスを作成します (または、C# を使用しているため、クロージャーを使用します)。
  2. インターフェイス pr を実装するオブジェクトのインスタンスを作成します。マッピングしたいメソッド
  3. ハッシュマップに追加します。

ディスパッチ ループ内:

  1. フェッチ番号
  2. ハッシュテーブルで番号を検索
  3. 見つかった場合はマップされたオブジェクトを呼び出し、そうでない場合は失敗

より柔軟なアプローチとして、番号を処理する必要があるメッセージと見なし、パターンの責任の連鎖を使用します。

コマンドパターンhttp://en.wikipedia.org/wiki/Command_pattern

責任の連鎖http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

于 2012-07-10T07:11:38.227 に答える
1

いつものように、多くの可能な答えがあります。クライアント/サーバー ゲームを構築しているときに、この問題に遭遇しました。アクション/イベント/メッセージの数十の Enum から始めましたが、より多くのアクションを実行すると、あまり持続可能ではないことに気付きました。

これが私が擬似コードで大まかに行ったことです。

    NetworkClass 
    {
       RegisterChannel(objUsingChannel,typeOfChannel, callbackForChannel, expectedReturnType){/*...*/};

       PushChannelMsg(channelID, dataToSend)

       ReceiveMessageFromNetwork(msg){  Read data from network, which also contains channelID, and send it to any matching channelID}  
       //If there is no channel registered for a data that is received,  just ignore it
    }

EnemyTurretObject
{
   //Register the rotation channel and listen for rotation changes
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=HandleTurretRotated, doubleReturnType)

   HandleTurretRotated(double rot)
   {  rotate the turret  }
}

FriendlyTurretObject
{
   //Register two channels that we'll send data across
   NetworkClass.RegisterChannel(this, channels.TurretFired + this.ID, callback=null, MissleFiredData)
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=null, doubleDataType)

   FireMissle()
    {
       NetworkClass.PushChannelMsg(channels.TurretFired + this.ID, new MissleFiredData(x,y)) 
    }

    RotateTurret()
    {
      NetworkClass.PushChannelMsg(channels.TurretRotated + this.ID, newTurretRotationValue) 
    }

}

私は基本的に列挙型の膨大な量全体を避け、より一般化されたセットアップを作成して、各オブジェクトが独自のデータ、チャネルなどを担当するようにしました。これははるかに柔軟なアプローチであり、1 つの列挙型を変更してもすべてが壊れることはありません。ネットワーク クラスは、送信されるデータに何が含まれているかを知る必要さえありません。これは単なる導管です。

于 2012-07-10T04:47:50.480 に答える