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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

オブジェクト指向

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

Q&A

解決済

3回答

17789閲覧

クラス設計の考え方についてヒントをいただけますでしょうか?

occhan

総合スコア19

C#

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

オブジェクト指向

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

1グッド

7クリップ

投稿2016/04/15 06:48

編集2016/04/20 13:20

長い文章で申し訳ありませんが、
設計(クラス分担の考え方)について教えていただければ幸いです。

ファイルA
ファイルB
ファイルC

を読み込み、それぞれに対して、

処理1
処理2
処理3

をした結果をクロス集計のように出力したいと思っています。

具体的な出力結果としては、

・ファイルAに対して処理1,2,3を行った結果を一覧出力。
(ファイルB,ファイルCも同じく)

・処理1をファイルA,B,Cへ行った結果を比較一覧出力。
(処理2,処理3も同じく)

と両方向から集計する感じです。

このとき、

・データの読み書きを担当するクラス

・データに処理を加えるクラス

・結果を出力するクラス

を作って進めようと思ったのですが、考え方は合っていますでしょうか?

悩んでいるのは、
「データの読み書きするクラス」でデータを配列に準備した場合、
その配列を「データに処理を加えるクラス」で使うには
引数で渡すことになると思うのですが、それが正しいのかどうかです。

特に、渡すべきデータ(配列)が多岐に渡っている場合は
引数で渡すのが正しいのか・・・
(引数は1~2つが妥当という意見とはかけ離れるので・・・)

もちろんクラスごと引数で渡してしまえば記述上は1つで良いのかもしれませんが、
こういう思考になること自体がクラスの役割分担などで
何か間違っているような気がして質問させていただきました。

オブジェクト指向(?)に慣れていない思考だと、
どのクラスからもアクセスできるグローバルなデータ用の配列を準備しておいて、
それぞれのクラスからアクセスすれば簡単なようにも思えます。
ただ、その方法だと仕様を拡張する時、
グローバルな配列をどんどん増やさなければいけない気もするので、
それはそれで何か違うような気がしています。

以前プログラムしていた言語が関数のスコープが緩く、
(下位の)呼ばれた関数が(上位の)呼んだ関数の変数はそのまま使えていたので、
どうして良いか分かっていないのもあります。

抽象的な質問で申し訳ありませんが、
アドバイスいただければ幸いです。
ご不明な点がありましたら補足しますので、
遠慮無くご指摘いただければ幸いです。

よろしくお願い致します。

※最後にプログラム的な表記も併記させてください。

c#

1Class データ読み書き 2{ 3 public string d_data = new string [1000]; //データ用配列 (実際は十数個) 4 5 public void データ読み込み (string ファイル名) 6 { 7 StreamReader sr = new StreamReader(ファイル名); 8 9 while (sr.Peek() > -1) 10 { 11 d_data[i] = sr.Readline(); // データ用配列にファイル内容を読み込む 12 i++; 13 } 14 } 15} 16 17 18Class データ処理 19{ 20 public void 処理1 (データ元) 21 { 22 処理1; 23 return; 24 } 25 26 public void 処理2 (データ元) 27 { 28 処理2; 29 return; 30 } 31 32 public void 処理3 (データ元) 33 { 34 処理3; 35 return; 36 } 37} 38 39void main 40{ 41 42 データ読み書き d1 = new データ読み書き; 43 44 d1.データ読み込み(ファイルA); // データ用配列に読み込む 45 46 データ処理 d2 = new データ処理; 47 48 d2.処理1(d1); // ←ここでd1の配列データを渡すために、 49 d2.処理2(d1); // d1(クラス)を引数で渡すことになる設計は正しいのか? 50 d2.処理3(d1); // データ処理のクラス内では、d1.d_data とアクセスしています。 51 52 ... 53 54} 55

ベストアンサーを付ける形のようなので、
tkow様に付けさせていただきましたが、
どの方の回答も非常に参考になります。

概略を書かせていただきますと、

unau様は 大きな枠組みでのクラス設計や実装
tkow様は クラス設計に加え、コンテキスト処理を含めたスマートな記述法
tkturbo様は クラスのインスタンスにおける static の利用やメモリの節約法

などが勉強できますので、後からこの質問を参照する方は、
概要を参考に回答を見ていただけると良いかもしれません。

ikemonnn👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

いくつか書きますが、C# は使ったこともないし、実行環境もないので、その点、ご了承くださいませ。

「クラスごと(インスタンスごと、のことかと思います)引数で渡してしまえば~役割分担などで何か間違っているような」とありますが、たとえ情報量の大きなインスタンスだとしても、それが論理的に考えて一つの情報のまとまりとなっているのであれば、そのインスタンスごと引数でやりとりするのはオブジェクト指向的に間違った考えだとは思いません。

クラス設計についてはいろいろな考え方があり、規模感や処理速度などの要求から一概には言えないのですが、他に制約がないのであれば、データを中心に考え、それにしかるべき処理をメソッドとしてつけたものをクラスとしてまとめるのがいいかな、と個人的には思っています。tkow さんが回答の最後に書かれているような設計方針ですね。

「データ読み書き」クラスがデータを保持しているのは、私的にはちょっと気持ちが悪いです。
名前の問題だけかもしれませんが、「データ」クラスがあって、それに「読み書き」メソッドがついている方が自然かな、と。あと、これは occhan さんが teratail に質問としてあげる際に元々の名前をわざと抽象的にしたのかもしれませんが、抽象的な「データ」クラスという名前よりは、たとえば、ファイル A が店舗 A の売り上げデータ、ファイル B が店舗 B の売り上げデータ、で日付ごとの売り上げデータが入っている、というようなものならば「売り上げ」クラスみたいな名前の方がいいのかな、と。
「読み書き」については、ストレージをファイルシステム (CSV) から RDBMS に替えるご予定があるとのことなので、メソッドにするのではなく、「ストレージ」クラスを基底クラスとして、「CSV」クラスと「SQL Server」クラスを派生クラスにするのはありだろうな、と思います(規模的にそこまでやらなくていいケースも多いですが)。「売り上げ」データを読み込む部分では「ストレージ」クラスのインスタンスを使うようにすれば、CSV から SQL Server に替えたときもその部分のコードは変更する必要がなくなりますので。

あと、処理1, 処理2, 処理3 については、これらの「処理機」or「タスク」or「コマンド」をクラス化するクラス設計もあります(これも、ここまでやらなくていいケースが少なくないです)。
つまり、「処理」基底クラスにたとえば exec というメソッドを定義しておき、「処理1」クラス、「処理2」クラス、「処理3」クラスをそれぞれ「処理」クラスの派生クラスにして、実際の処理を exec メソッドをオーバライドする形で作ります。

あとは、具体的なストレージの種類や設定 (SQL Server であれば接続パラメータとか) を渡すとか設定ファイルや環境変数を読み込んで具体的なストレージインスタンスを生成する関数 (下の例では getStorage) や、各売り上げデータに施すべき処理の「タスク」をリストとして返す関数 (下の例では getTasks) を作ったりして、次のようにするとか。

c#

1static void main() { 2 ストレージ storage = getStorage( /* ここになんか設定とか */ ); 3 List<処理> tasks = getTasks( /* ここになんか設定とか */ ); 4 List<売り上げ> saleList = getSaleList( /* なんか */ ); 5 for (saleList のすべての要素 sale に対して) { 6 sale.load(storage); 7 for (tasks のすべての要素 task に対して) { 8 task.exec(sale); 9 } 10 } 11}

済みません、ちょっと力尽きて最後雑になりました。
おそらくやりたい規模からしたらリッチすぎるやりすぎなクラス構成で、パフォーマンス面ではよくないものですが、クラス設計の考え方をいろいろお知りになりたいご様子でしたので口出しいたしました。

投稿2016/04/17 10:14

unau

総合スコア2468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

occhan

2016/04/20 13:13

unau様、ご回答いただき、ありがとうございました! また、返信が大変遅くなりまして、誠に申し訳ありません。 非常に大きな枠組みでのお話をいただき、すごく勉強になりました。 お陰様で、自分の中で大丈夫な部分と要修正な部分がハッキリとしました。 情報量が大きいとしても論理的に考えて1つのまとまりなら 引数として扱っても良い。 ここが迷っていた原因でもあったので、スッキリしました。 クラス名に関しては、 ご指摘の通り簡略化してしまったのですが、 「データ」クラスに「読み書き」メソッド というのが自然なのは大きく頷けますので、 そのあたりの表現の修正も含めて再構築していきたいと思います。 またストレージを入れ替える件での方法を教えていただき ありがとうございます。 なるほど、「クラスを派生させる」のは 機能としては知っていましたが、 使い方の理解が追いついていなかったので、 こういう場合に利用すれば良いのですね。 これから勉強してみます。 処理の件もクラス化にすると整理されて良さそうですね。 基底クラスからのオーバーライド、実装してみたいと思います。 ちなみに処理の種類としては100種類くらいあるのですが、 その中から条件によって10種類くらいを抜き出して適用させる形になります。 なので、デリゲートで処理をする関数をリストに入れて、 それをループで実行させようかな、と考えています。 (↑勉強途中で書いているので、全くトンチンカンかもしれません。) もう少し大きなプロジェクトの場合の考え方を伝授いただき、 ありがとうございました! こういうお話は言語の本とかには載っていない(と思う)ので、 大変勉強になりました。 最後に返信が遅くなってしまったこと、 重ねてお詫びさせていただきます。 申し訳ありませんでした。 そして、ありがとうございました!
unau

2016/04/20 14:05

処理の数が 100 種類くらいある、そしてその中から 10 種類くらい抜き出して適用する、ということであれば、処理クラスの導入は向いていると思います。
occhan

2016/04/21 12:47

unau様、ふたたびの回答ありがとうございます! 処理クラスの導入についてのフォロー助かります。 詳しい方に後押しされると自信を持って進められますので、 処理もクラス化して頑張ってみます。 この度は本当に回答ありがとうございました!
guest

0

ベストアンサー

class設計の一つの基準はそのインスタンスを複数生成する必要があるかないかです。
一つ定義されていればデータの内容を変える必要がないものはクラスにする必要性はほとんどありません。
例外としては,staticメソッドのみをグループ化するライブラリなどを作る場合,static Classで実装することがあります。このとき,インスタンスを作成したり,定数以外の内部変数を処理するような実装にしてはいけません。なので,定数は,readonly属性を作るようになると思います。

質問者様がデータ処理のクラスを他のコードで使い回す可能性があるのなら,処理のクラス化はokです。
C#では,Listを使えるので,いくらデータが入るかわからないものは配列ではなくListにしましょう。
arrayとListは相互に変換できるので,Listの方が無難です。

また,コレクションの要素一つ一つにやる処理に対して,引数を配列にするのはやめましょう。なぜなら,Listやarrayには,関数を引数に取ると,要素の一つ一つにその関数を適用して要素を返す関数が存在したり,処理を並列かするのが簡単に出来るからです。

個人的にはこういう流れで書きます。実行環境なしで書いているので,文法のミスなどがあったら調べて直してください。また最適とまではいかない可能性があるので参考までにしてください。namespaceなどは省略しています。

C#

1Class データ読み書き 2{ 3 public List<string> d_data {get;,set;}; //データ用list 4 public データ読み書き(string[] input){ 5 d_data = new List<string>(); 6 foreach(var file_name in input){ 7 データ読み込み(file_name) 8 } 9 } 10 public void データ読み込み (string ファイル名) 11 { 12 (using StreamReader sr = new StreamReader(ファイル名)){ 13 string temp=""; 14//仕様だとファイルごとの処理と書いてあったのでファイルごとにしました。行ごとにしたければ二次元Listになおしてください。 15 while (!sr.EndOfStream) 16 { 17 temp += sr.Readline(); // データ用配列にファイル内容を読み込 18 } 19 this.d_data.push(temp); 20 }; 21 } 22} 23 24 25Class データ処理 26{ 27 public void 処理1 (データ元) 28 { 29 処理1; 30 return データ元; 31 } 32 33 public void 処理2 (データ元) 34 { 35 処理2; 36 return データ元; 37 } 38 39 public void 処理3 (データ元) 40 { 41 処理3; 42 return データ元; 43 } 44} 45 46 static void main(string args[])//argsでファイル名指定 47{ 48 using static データ処理; 49 データ読み書き d1 = new データ読み書き(args); 50 51 var data_list = d1.d_data.Select(element=>{ 52 //書き直しました 53 element=処理1(element); 54 element=処理2(element); 55 return 処理3(element); 56 }).ToList(); 57 58 59 ... 60 61} 62

また,処理の対象がデータが一種類しかしないのであれば処理クラスの処理をデータ読み書きのクラスに移してデータクラスとしたほうがオブジェクト指向っぽくなります。個別のデータが自分自身のデータを直接操作できるほうがいい場合オブジェクト指向に向いた設計になります。

C#

1Class データ 2{ 3 public string d_data {get;,set;}; //データ用list 4 public データ(string input_file){ 5 d_data =""; 6 (using StreamReader sr = new StreamReader(ファイル名)){ 7 string temp=""; 8//仕様だとファイルごとの処理と書いてあったのでファイルごとにしました。行ごとにしたければListになおしてください。 9 while (!sr.EndOfStream) 10 { 11 d_data += sr.Readline(); // データ用配列にファイル内容を読み込 12 } 13 処理1(); 14 処理2(); 15 処理3(); 16 17 }; 18 } 19 20 21 public void 処理1 () 22 { 23 d_dataの処理 24 } 25 26 public void 処理2 () 27 { 28 d_dataの処理 29 } 30 31 public void 処理3 (データ元) 32 { 33 d_dataの処理 34 } 35} 36 37 38 static void main(string args[])//argsでファイル名指定 39{ 40 List<データ> data_list=args.Select(element=>new データ(element)).ToList(); 41} 42

投稿2016/04/15 08:39

編集2016/04/17 10:35
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

occhan

2016/04/15 09:08

tkow様、早速の回答ありがとうございました。 実は席を外す用事が出来てしまったので、 後でゆっくり理解してから再度コメントさせてください。 書き込むまで時間が空いてしまいそうなので、 取り急ぎお礼まで書かせていただきました。 ありがとうございました!
occhan

2016/04/16 06:15

tkow様、詳細な返信が遅くなりまして申し訳ありませんでした。 この度は色々な方面から詳細な説明をいただき、誠にありがとうございました。 「class設計の基準はインスタンスを複数生成する必要があるか?」 すごく明快な基準で助かりましたので、今後の指針とさせていただきます。 今回の件ですが、実は他のコードで使い回す可能性は低いです。 なので、関数で処理するというカタチでも良いのかもしれませんね。 一応、クラスにした経緯ですが、 データを保存する配列をファイル毎に用意したいので、 保存用の配列と読み書きのメソッドがクラスでパッケージになっていたら 頭で整理するのが楽かなと思って作りました。 また、現在ファイル入力形式はcsvなのですが、 いずれはSQL Serverへの移行を考えていて、 その時、データを読み込んで配列にするクラスは入れ替えられたら楽かも・・・ なんていう思惑もありました。 (本来は最初からSQL Serverで組めば良いのでしょうが、 趣味でやっているので、SQL Serverの勉強までは手が回らず、 とりあえずの結果が欲しくcsvのまま進めています。 ファイルが6000個(データもファイルごとに50x10000)を越えているので、 移行したほうが良いとは思うのですが・・・) 次に、配列ではなくLISTを使うご指摘ありがとうございます。 確かにLISTを使ったほうがファイルの大きさにも柔軟に対応できますし、 フィルター処理なども出来るので良いですよね。 単純に「配列=早い」「リスト=遅い」なんて情報をどこかで見たものですから 配列で組んでしまっていました。(実際にはデータClassの配列です。) アドバイスを受けて、 配列は速くLISTは遅いか?を実際に試したところ、 約6000個のファイルに対し、1ファイルにつきデータ3000行を読み込む時、 ・データClassの配列を予め用意しておき代入する。 ・データClassのLISTを作成し、add(new(初期化子))で追加していく。 の2通りで試したところ、 配列 = 16.435 秒 LIST = 16.903 秒 という結果で、速度を気にしてLISTを避ける必要は無いと分かりました。 そしてお陰様で便利なLISTへ書き直すことが出来ました。ありがとうございました。 最後になりましたが、実際の処理の仕方について、 LINQなどを使った例をご教授いただき、ありがとうございます。 すごくシンプルになっていて目から鱗です。 ラムダ式(?)などを使うとコレクションを処理するのは 本当に分かりやすく書けるのですね。 いただいたコード例は、 「C# 3.0を活用してスッキリしたコードを書く」 という目標そのものなので、 参考にして頑張ってみたいと思います。 もう1つだけ教えていただいても宜しいでしょうか? 実は後出しの情報で恐縮なのですが、 今回のデータ処理、 行ごとに日付のデータになっていて、 処理の途中に過去のデータも参照する必要があります。 (d_data[30] を処理している時に d_data[29] や d_data[28] を参照する。) Linq などで処理する時、 ・特定の順番が保証されている ・該当行以外のデータを参照する というのは可能でしょうか? 前者はsortで、 後者はIndexを自分のデータ列の1つとして持っていれば 実現できそうな気もするのですが、 何かアドバイスいただければ幸いです。 この度はアドバイスありがとうございました! 自分のスキルが1段上がる道しるべをいただけた気分です。 あとは「1段上がった気分になっただけ」にならないよう 自分のものにするべく頑張ります!
退会済みユーザー

退会済みユーザー

2016/04/17 11:12

ご返信遅れて申し訳ないです。 C#3.0で書かれているのですね。現在C#6.0までリリースされているので開発環境的に無理でなければC#6.0で開発されることをお勧めします。 .netframworkも4.5以上とそれよりも下のバージョンでは機能の充実性がかなり違うので,できれば上の方のversionを使う方が簡単に早くスッキリ書けてお勧めできます。 >一応、クラスにした経緯ですが、 データを保存する配列をファイル毎に用意したいので、 保存用の配列と読み書きのメソッドがクラスでパッケージになっていたら 頭で整理するのが楽かなと思って作りました。 オブジェクトというデータ構造にすることで実態が捉えやすいものになる場合は,一つ以上インスタンス生成しないようなものでもクラスにするのはありです。例えば,リモコンという機械は一つ以上必要ありませんが,リモコンというクラスの概念を作ると,リモコンの機能が想像しやすいというのであれば,クラスにするのは良い設計です。なのでそのような意図で作られたのであれば使い方としては問題ありません。 あと,C#はクエリを直書きできますし,LinqはほぼSQL構文をC#のデータ構造に適用するだけでなく,データベースアクセスの時にも使えます。なのでLinqで書くことに慣れていると,SQLを使わなくても,簡単にデータベース操作することができます(C#の最も良い利点の一つだと思います)。MySQLServer使う場合もLinqの構文は生きるので是非覚える都良いと思います。 また, > データを読み込んで配列にするクラスは入れ替えられたら楽かも これは,ジェネリックという機能を用いると可能です。また,VisualStudioは独自にO/Rマッパーを持っており,データベースの構造からC#用のクラスを自動生成してくれる機能があります。こちらのクラスに共通の独自のInterfaceの型を継承させることで, void データ読み書き(独自のInterfase型 data){}; など引数にInterfaseの型をとる関数として実行することで,インターフェースを継承しているオブジェクトを全て同じ関数の引数に入れて実行できるようになります。 >特定の順番が保証されている 使ったことないので名前がすぐに出てきませんがLinqまたはquery構文はデータベース操作を完全にサポートしているので,decend by,accend byに当たる関数があると思います。また GroupByや,Whereで絞り込みもできます。 >d_data[30] を処理している時に d_data[29] や d_data[28] を参照する。 >該当行以外のデータを参照する ラムダ式の引数を二個とると,第2引数が現在参照している要素のindexをとることができるので,ラムダ式の処理内でそのindexを基準に,他の要素にアクセスしたり,ラムダ式は関数スコープになるので,絶対にアクセスしたい列要素のindexを定数で指定したりということは可能です。また,調べてみると結構関数があるもので,二次元操作くらいであれば,indexを直指定で操作する関数がC#自体にあるかもしれません。 とはいえこういう処理は可読性を下げてしまうこもあるので,関数の引数を配列にlistにしてindex操作で処理した方が無難かもしれないです。 是非頑張ってください。
退会済みユーザー

退会済みユーザー

2016/04/17 11:21

またいうまでもないかもしれませんが、フォルダの中のcsvを全部指定するみたいなこともできるのでcsvで試したいときはその方法を調べてみるといいと思います!
occhan

2016/04/20 13:00 編集

返信が大変遅くなりまして、誠に申し訳ありません。 > C#3.0で書かれているのですね。 > 現在C#6.0までリリースされているので > 開発環境的に無理でなければC#6.0で開発されることをお勧めします。 申し訳ありません・・・ ラムダ式などを調べるとC#3.0からだったので、 C#3.0という表現をしてしまいました。 制約さえ無ければ、やはり最新の環境が一番ですよね。 > オブジェクトというデータ構造にすることで > 実態が捉えやすいものになる場合は, > 一つ以上インスタンス生成しないようなものでも > クラスにするのはありです。 これを聞いて安心しました。 自分の設計が不安で質問させていただいたのですが、 ひとまず全くの間違いでは無かったようで良かったです。 Linqの件についてもメリットを分かりやすく教えていただき、 ありがとうございました。 今まで、ACCESS + VBA だったため、 SQLもウィザードで作られたものを手打ちで直す、なんて事をしていましたが、 教えていただいたメリットは非常に魅力的だったので、 SQLの勉強も含め、Linqの構文をもっと勉強していきたいと思います。 > こちらのクラスに共通の独自のInterfaceの型を継承させることで, > > void データ読み書き(独自のInterfase型 data){}; > > など引数にInterfaseの型をとる関数として実行することで, > インターフェースを継承しているオブジェクトを > 全て同じ関数の引数に入れて実行できるようになります。 Interfaseの機能を理解していないので、 想像なのですが、読んだだけでも非常に便利そうなので、 実現するべくやってみます。 ここは勉強が全く追いついていないので、 理解した結果をここにご報告できないのですがご容赦ください。 ちなみに、今回のInterfaseなどもそうなんですが、 機能単体がそれぞれ説明されているページを見ても、 「自分の処理にどう当てはめるか?」って意外と分からないので、 こういった助言は本当に助かります。 質問させていただいた問題にも回答いただき、 ありがとうございました。 ■Linqを使う際に特定の順番が保証されている件 ヒントをいただいたaccend by等から調べたところ、 orderby 句 で実現できそうです。 勉強途中なので他サイトからの引用で恐縮ですが、 ```lang-c# public ActionResult Orderby() { var articles = db.Articles .OrderBy(a => a.Published) .ThenBy(a => a.Title); return View(articles); } ``` OrderBy      昇順にソート OrderByDescending 降順にソート ※さらに絞り込む場合(第2キー以降) ThenBy      昇順にソート(第2キー以降) ThenByDescending 降順にソート(第2キー以降) 出典元:LINQ:データを並べ替える - orderby句[C#]2014.11.18 http://www.buildinsider.net/web/bookaspmvc5/050303 となるようです。 ■Linqで該当行以外のデータを参照する件 > ラムダ式の引数を二個とると, > 第2引数が現在参照している要素のindexをとることができる まさにLinqを使う上で求めていた情報です。 いただいた情報の例を探したところ、 きちんとMSDNにありました。(汗) 自分で探している時には「どんな方法があるか分かっていない」 ので見つけられませんでした。 ```lang-c# var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index); ``` ここのindexですね。これを基準にすれば、1つ前などを参照できますね。 出典元:ラムダ式 (C# プログラミング ガイド) https://msdn.microsoft.com/ja-jp/library/bb397687.aspx ただし、tkow様も仰る通り、可読性を考えると、 この方法でLinqを使うのは微妙なのかもしれませんね。 ちなみにLinqを多次元配列に適用できるようにする件、 直接的な関数は見つけられなかったのですが、 先人の皆さんが色々な方法を考え出してくれているようです。 ・1次元のように扱って良いなら castする。 ・多次元を維持するなら拡張メソッドを作ってみる。 などがあるようです。 出典: LINQ で多次元配列を使用する方法 https://social.msdn.microsoft.com/Forums/ja-JP/e1af278d-2239-4dc7-a3fc-995f3062f658/linq-?forum=csharpgeneralja 多次元配列を LINQ で簡単に扱おう http://blog.xin9le.net/entry/2016/02/17/003605 ※なお、検索した結果を書かせていただいたのは、 ココを他の同じ疑問を持った人が見た時のため、 そして自分が整理するためです。ご了承ください。 この度は色々なヒントや回答をありがとございました!
guest

0

C#

1public void データ読み込み (string ファイル名)

細かいことを言うと、こういう処理はstaticでやったほうが処理が早かったりします。
また、この構成だと「Class データ読み書き」がデータの保持もしてしまっているので、

C#

1// インスタンス変数 2public string d_data = new string [1000]; //データ用配列 (実際は十数個) 3public long d_size = 0; // 実際に読み込んだファイルの行数 4// インスタンスを生成するstaticメソッド 5public static データ読み書き データ読み込み結果取得(string ファイル名){ 6 データ読み書き d = new データ読み書き(); 7 // 省略 8 while (sr.Peek() > -1) 9 { 10 d.d_data[d.size] = sr.Readline(); // データ用配列にファイル内容を読み込む 11 d.size++; // 読み込んだ行数をインクリメント - 処理のほうで最終行を知るのに使用 12 } 13 return d; 14}

のように取り扱うとよいかも。

あとは呼び出し側で

C#

1データ読み書き d1 = データ読み書き.データ読み込み結果取得(ファイルA); // データ用配列に読み込む 2データ読み書き d2 = データ読み書き.データ読み込み結果取得(ファイルB); // データ用配列に読み込む 3データ読み書き d3 = データ読み書き.データ読み込み結果取得(ファイルC); // データ用配列に読み込む

みたいにしてやるとd1,d2,d3に読み込み済みデータとデータ行数が設定された状態で取得できますね。

これを対象の処理に引数渡しする分には何も問題ないと思いますが。

C#

1// データ処理クラス側の定義 2Class データ処理 3{ 4 public void 処理1 (データ読み書き d) 5 { 6 for(int i = 0; i < d.size; i++){ 7 // 各行に対する処理1; 8 } 9 return; 10 } 11// 略:以下、使う側の記述 12 処理1(d1); // ファイルAの読み込み結果に対して処理1を実行 13

投稿2016/04/15 07:43

編集2016/04/15 08:25
tkturbo

総合スコア5572

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

occhan

2016/04/15 09:15 編集

tkturbo様、迅速な回答ありがとうございました。 大量のデータを引数で受け渡しすることは問題無いのですね。 安心しました。 また、static への改善案もありがとうございます。 (読み込んだ行数へのフォローがさりげなくしてあるところが脱帽です。) お陰様でいただいた改善案のコードも反映させ、 無事に動作させることが出来ました。 (同じような処理をする場合には、1つのメモリ領域を確保するstaticのほうが 迅速になる、という理解で宜しいでしょうか?) ただ、まだ勉強不足のため、以下の自分が書いたコードと データ保持している部分が違うというのが理解できていません。 一番の相違点は new する場所が クラス内なのか、呼び出し側なのか、 という部分だと思うのですが、 違いをご教授いただけたら嬉しいです。 もし、お時間に余裕がありましたら、もう少しお付き合いいただければ幸いです。 Class 自分 {   public string d_data = new string [1000];   public long d_size = 0; // 実際に読み込んだファイルの行数   public void データ読み込み(ファイル名)   {     while (sr.Peek() > -1)     {       d_data[d.size] = sr.Readline(); // データ用配列にファイル内容を読込       d_size++; // 読み込んだ行数をインクリメント     }   } } Class tkturbo様 {   public string d_data = new string [1000];   public long d_size = 0; // 実際に読み込んだファイルの行数   public static tkturbo様 データ読み込み結果取得(ファイル名)   {     tkturbo様 d = new tkturbo様;     while (sr.Peek() > -1)     {       d.d_data[d.size] = sr.Readline(); // データ用配列にファイル内容を読込       d.d_size++; // 読み込んだ行数をインクリメント     }   return d;   } } main {   //自分のクラスの場合   //データへのアクセス aaa.d_data[i] , aaa.d_size   自分 aaa = new 自分;   aaa.データ読み込み(ファイルA);   //tkturbo様のクラスの場合   //データへのアクセス bbb.d_data[i] , bbb.d_size   tkturbo様 bbb = tkturbo様.データ読み込み結果取得(ファイルA); } もちろん、tkturbo様の記述のほうがスマートなので、 そちらを使わせていただきたいのですが、 自分の間違いをもう少し理解したくて書かせていただきました。 細かい点になることご容赦いただければ幸いです。 最後にもう1度、ご回答ありがとうございました!
tkturbo

2016/04/15 09:39

インスタンスは新規生成(new)されるタイミングで新しいメモリアドレスが割り当てられます。 私の提示したソースのd1とd2は別アドレスに記憶されるのでデータが入り交じることがないのです。 また、インスタンスメソッド処理用の領域もインスタンス生成時に確保されるので、不要なインスタンスメソッドを排除することでメモリ使用量が削減できます。 staticメソッドはクラスがloadされるタイミングでメモリアドレスが割り当てられるのでインスタンスの増加によるメモリへの影響を緩和できるのです。
occhan

2016/04/15 13:23

tkturbo様、度重なる質問に回答いただき、ありがとうございます。 なるほど、メモリの増加を抑える効果があるのですね。 それが(早い以外にも)static を使われた理由なのですね。 staticを使う。 クラス内で new する。 教えていただいたポイントを参考にして、 もっと勉強していきたいと思います。 何度もご回答いただき、本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問