質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.76%

  • C#

    6563questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • オブジェクト指向

    271questions

    オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

  • 継承

    37questions

    継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

C# クラスについて

解決済

回答 4

投稿

  • 評価
  • クリップ 1
  • VIEW 364

START_

score 15

現在C#の勉強を少し前から始め、
フォームアプリを使いチャットやソケット通信などをしております。

クラスやオブジェクト継承などについて
最初に軽く勉強はしたのですが(とは言っても、こんな感じかぁ...程度です)
実際にコードを書いていくうちに、
ひとつのクラスがメソッドだらけでとても長く見づらいものとなってしまいました。

そこでこれから先、もし同じようなプログラムを組むときに、
使い回し等が効くようにクラス分けしたいと思いました。

例えば今やっているシリアル通信で、
1.今回のアプリで使う具体的な数値や設定
2.使い回しのきく、送受信の処理
3.使い回しのきく、COMポートをOPENする処理
4.メッセージボックスにて使う処理
のようにクラス分けするのかな?という考えに現在至っているわけですが、
相違があれば教えていただきたいです。

また、使い回す際のコードの書き方がいまいちわからないです。
例えば、

 recData += System.Text.Encoding.ASCII.GetString(buffer);
            var temporary = Sendbuffa + recData;
            while (true)
            {
                newline = temporary.IndexOf(Environment.NewLine);
                if (newline >= 0)
                {
                    string[] lines = temporary.Split(
                        new[] { Environment.NewLine },
                        StringSplitOptions.None
                    );
                    logText(lines[0], ComPort);

                    if (lines.Length > 1)
                    {
                        // temporaryに残りを詰めなおす
                        temporary = temporary.Substring(newline + 2);
                    }
                }
                else
                {
                    Sendbuffa = temporary;       //データをいったん保存
                    break;
                }
            }

このコード(前回質問させていただいた時の教えていただいたコードなのですが...)を
使い回しのきくクラスとメインの今回のアプリで使うクラスに分けるには、
どのようにすればいいのでしょうか?

気をつける点やポイント等教えていただきたいです。
長くなりましたがよろしくお願いいたします。

[++C++;//未確認飛行C]
http://ufcpp.net/study/csharp/oo_class.html
こちらのサイトも参考にさせていただいたのですが自分の知識がついてこず..
いまいちピンと来てくれませんでした :(

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+4

このコードに使いまわせる部分は無いと思います。

追記

使いまわすならこんな風に書きます。

public static IEnumerable<string> SplitLines(byte[] buffer, Encoding encoding)
{
    using (var reader = new StringReader(encoding.GetString(buffer)))
    {
        while (true)
        {
            string result = reader.ReadLine();
            if (result == null) break;
            yield return result;
        }
    }
}

もっと簡単にこうも書けます。Encoding は省いて文字列対象にしました。
バイトデータから文字列を取り出すのは上記のように簡単なので。

static IEnumerable<string> SplitLines(string str)
{
    return Regex.Split(str, "\r?\n");
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 19:30

    コメントありがとうございます。
    こんなにコードが簡潔でとても見やすくなるんですね!!
    参考にさせていただきます!

    キャンセル

+3

通信部分に関してはよくわからなかったのですが、要は区切り文字毎にLogに書き出すという内容と理解しました。

であれば、そのように書くのがオブジェクト指向の醍醐味です。

再利用性はあまり考えない方がクラス分けはしやすいです。
例えばこんなのはどうでしょうか?
(あくまで例です。雰囲気をつかんでもらえれば・・・)

var receiver = new XXRciver(env);
receiver.Receive((string line) => {logText(lines[0], ComPort);});

1行目は、動作に関して初期値を与えています。
引数は一つになっていますが、実際はたくさんあったり、それをまとめたクラスだったりを入れます。

XXRciverの中身はクラスの設計時には気にしないようにしてください。

2行目は、受信時の動作を定義しています。
レシーバーは受信するのが目的のオブジェクトです。
受信した後の動作は、外部から指定してあげる必要があります。
ループの内側にある部分なので、処理を渡してます。

ラムダ式に慣れていないなら、インターフェイス越しに書きこんでもいいです。
(このインターフェイスは再利用のためというわけではなく、受け取り側がどのメソッドを気にして、どのメソッドを気にしていないのかを宣言するためのものです。場合によっては不要です。)

interface IAtRecieved{
    void AtRecieved(string line);
}

class LogWriter : IAtRecieved{
    void AtRecieved(){
        logText(lines[0], ComPort);
    }
}

このように関心事を明確にして、簡潔に記述し段階を分けることを主眼としてください。

適切に分割されていれば、再利用が可能が場合もあると思います。

再利用を目的として書くとどうしても”かもしれない”で余分な機能を作りこみます。しかし、大体において予想外の機能が必要になるため、余分な機能がてんこ盛りのクラスの変更が大変だということになりますのでご注意を。関心事ごとに分割されているのが大事です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 10:37

    コメントありがとうございます。
    おっしゃる通り再利用のことばかり考えておりました。。
    これからは、事柄ごとに分割してすっきり見やすいコードかけるように頑張ります!

    キャンセル

checkベストアンサー

+2

a. コードで気になった点。
1, 受信処理の解析と思われる部分に変数名:Sendbuffaがあるのが気になります。
送受信データの混在を避けるため送信バッファ/受信バッファの変数は分けると思いますが、単純に変数名の命名がそうなっていただけでしょうか?

var temporary = Sendbuffa + recData;


2,temporary.IndexOftemporary.Splitを同時に行っているのが気になります。
whileループからforループに変換して、以下のコードでも。

var temporary = Sendbuffa + recData;
var lines = temporary.Split(new[] { Environment.NewLine },
     StringSplitOptions.None
     );
for (int i=0; i< lines.Length - 1; i++)
{
    logText(lines[i], ComPort);
}
Sendbuffa = lines.Last();


3,デミリタ(区切り文字)にEnvironment.NewLineを使っている点
参考 ->environment.newline

UNIX 以外のプラットフォームでは "\r\n" を含む文字列。UNIX プラットフォームでは "\n" を含む文字列。

Unixプラットフォームではenvironment.newlineの改行コードが違うため、このプログラムをUnixで動かした時に注意が必要です。


b. クラス分けについて
SerialPortServerクラスを新規作成して、2)と3)の処理を行います。

4)はクラスを作らずにAction<string>SerialPortServerクラスに定義します。
参考 -> Action<T> デリゲート

public Action<string> logWrite; 


呼び出し方
logWrite(lines[0]);

画面を更新する必要がある時は、Actionイベント内でコントロール名.Dispatcher.BeginInvoke();を呼び出すのをお忘れなく。

最後に通信に関わる部分は通信プロトコル解析処理(Parser)がどうしても必要になるので、使い回しはしずらいと思いますー。
自作せずにHTTPなどのプロトコルを上に被せる形がいいのではと。
どうしても自作するなら、状態遷移図と通信プロトコルのドキュメントは最初に作成するほうが後々苦労しないです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 19:27 編集

    コメントありがとうございます!
    まずバッファ変数名については、自分の命名が下手でした。。
    「送られて来た」ということでsendにしていましたが、今考えると変ですね。。。
    まだプログラミング勉強し始めたばかりで、C言語C++そしてC#の順に勉強しており、「これからのことを考えてこうするといいよ。」というのは本当に助かります!(やっぱりやるからにはこれからの事を見据えた勉強をしていきたいと思っておりますので : )
    ありがとうございました!!

    キャンセル

+2

別解をひとつ。

前の質問で、バイナリ通信にこだわられてたようなので、

  • バイナリ受信クラス
    バイナリデータ列をリングバッファやStreamかなんかで保持
    バイナリデータによりIndexOfメソッド、
    先頭からnバイト取得/削除するメソッド
  • バイナリデータを文字列に変換するクラス
    おそらくStringBuilderあたりをつかって、
    バイナリ受信クラスに改行コードのバイト文字列を与えて先頭データ列を取得、文字列に変換して返す
    存在する行を全部string配列/Enumerableにして返すメソッド

まあ、こういうふうに分ければ、バイナリ通信の場合に使いまわしできるし、バイナリ-文字列変換ってのも他に使いまわしできるように思います

#再利用できるように、ちとムリクリですがw

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/15 10:26

    なるほど。結構細かく分けるのですね!
    やっと少しは分け方がわかって来た気がします。
    用途ごとややっていることの種類が同じようなことをまとめてクラス分けのような感じで徐々にできるようになります!
    ありがとうございます!!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.76%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    6563questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • オブジェクト指向

    271questions

    オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

  • 継承

    37questions

    継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。