restofwaterimpのぎじゅつMemo

SIerに所属。企画から運用まで幅広くやってます。C#中心に書いてます。

【C#】C#プログラミングのイディオム(9章)ファイル操作

前回の続き

restofwaterimp.hatenablog.com

今回からは実践編に。 最初はファイルの操作。 アプリを構築するには外部ファイルとのやりとりがほぼある。 結構、例外とまでは行かないが、段階を踏んでファイルの読み書きをしないといけないが、 旧来のようにtry-catch-finallyではなくusingを利用しようという箇所もあった。

ファイル入力

var filePath = @"/Users/itoutomokazu/Documents/Testing/input.txt";

            /*昔はtry-finallyでリソース解放のため、Dispose()をしていた。今はusingで囲うみたい*/
            if(File.Exists(filePath)){
                //普通はEncoding.UTF8でいけるがshift_jisはGetEncodingを利用する
                using (var reader = new StreamReader(filePath, Encoding.GetEncoding("shift_jis"))) {
                    while(!reader.EndOfStream){
                        var line = reader.ReadLine();
                        Console.WriteLine(line);
                    }
                }
            }
        }

Fileのメソッドを利用する場合はReadAllLinesを利用することもできる。

docs.microsoft.com

ただ、読み切るまで、処理待ちが発生すること。 また、一度にメモリを利用するので、あまり大きなものは処理速度低下に繋がるようである。

.net 4.0以上では File.REadLinesを利用できるようです。

利用 StreamReader File.ReadAllLines File.ReadLines
戻り値 StreamReader string[] IEnumerable
取得タイミング StreamReader.ReadLine()で1行ごと ReadAllLinesでファイル全て 利用する時のみ
LINQで加工 不可 不可

/*最初の3行だけ取得*/
            var top3ines = File.ReadLines(filePath).Take(3);
            foreach(var line in top3ines)
            {
                Console.WriteLine(line);
            }

.netFrameworkが4.0以降を利用しているなら、積極的にReadLinesを利用したほうが良さそう。

ファイル出力

1行ごと出力する方法

            using(var writer = new StreamWriter(filewritePath)){
                writer.WriteLine("Get away , this floor");
                writer.WriteLine("Poo is a man.");
                writer.WriteLine("Where is you");
            }

            /*ファイルに値を追加をする場合には、appendを利用する。記載しないと上書き*/
            using (var writer = new StreamWriter(filewritePath, append:true))
            {
                writer.WriteLine("Get away , this floor");
                writer.WriteLine("Poo is a man.");
                writer.WriteLine("Where is you");
            }

docs.microsoft.com

複数行を一度の出力する場合、ファイルの読み込みと同じで、 File.WriteAllLinesを利用する。

既存のメソッドの利用では、ファイルの上書きまたは末尾に追記しかできない。 ファイルの先頭や間に差し込むことは自前で処理を用意する必要があります。 書籍には、例として、先頭に行を挿入する場合が記載されています。

            using (var stream = new FileStream(filewritePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)){
/* ファイルを途中でクローズせず、やりきること。閉じるとクリアされるから*/
                using (var reader = new StreamReader(stream)) 
                using (var writer = new StreamWriter(stream)){
                    string text = reader.ReadToEnd(); // ファイルの最初から最後まで読み,textに格納
                    stream.Position = 0;              // ポジションを先頭にする
                    writer.WriteLine("new line");     // 書き込み
                    writer.WriteLine("new Line !");
                    writer.Write("text");             // 読み込んでおいたtextを最後に書き込み
                }
            }

あるところに足すと言うより、一度退避して、再度追加したい内容とともに書き込みと言う感じです。 間に挟むとかなら、ターゲットとなる箇所までと、ターゲットとなる箇所以降で退避し、 間に挿入したい値を入れるとか。

ファイルの操作

ファイルの読み書きと同じように、ファイルを読み書きする前の確認もよく使います。 System.IO名前空間のFIle、FileInfo クラス が記載されている。

Fileを使うべきか・・・FileInfoを使うべきか・・・ということもテーマに上がっている。

書籍に書いてあるように、使っているのはFIleクラスを利用することがおおいな〜と思います。

ファイル入出力 (System.IO.File) - Programming/.NET Framework/ファイルシステム - 総武ソフトウェア推進所smdn.jp

docs.microsoft.com

docs.microsoft.com

見ると、Fileはstaticクラスなので、インスタンス化(new)が不要で、FileInfoはそうではない。 コードが少ない方、また例外処理の発生(ファイルがない・・・など)をうまく捕まえやすい方を使うのがいいかと。 いつもはFileクラス使ってますが。

ディレクトリの操作

ファイルの操作と同じくらい、ディレクトリの操作も使います。 業務アプリではファイルがあるか・・・というときに、そもそも該当のディレクトリがあるの? から始まりますので、フルパスをもらったら、Windows系なら¥でディレクトリを区切りながら フォルダチェックを行うことになります。

こちらもファイルと同じく、System.IO名前空間に DirectoryクラスとDirectoryInfoクラスがあります。

Fileと同様、基本はstatic なDirectoryクラスを利用します。

色々見て見ると、1つのフォルダやファイルに対し、複数操作を実施することがあるならば、 xxxInfoを。一つの操作のみで完結するならば、InfoをつけないFileやDirectoryを利用すると良いとのこと。 (ソース常に同じファイルをやディレクトリを何度も記載しなくて済むようにとのこと)

どちらもWindowsならdirやcd、moveなどのコマンド。 macならpwd,ls,cd,などのコマンドに相当することができます。

            /*ディレクトリに存在チェック*/
            if (Directory.Exists(@"/Users/xxxx/Documents/Testing"))
            {
                Console.WriteLine("フォルダある");
            }

            /*現在のディレクトリ取得*/
            var curdir = Directory.GetCurrentDirectory();
            Console.WriteLine(curdir);

            var di = new DirectoryInfo(@"/Users/xxx/Documents/");
            DirectoryInfo[] directories = di.GetDirectories().Where(x => x.Name.Length >=10);
            foreach(var dinfo in directories){
                Console.WriteLine(dinfo.FullName);
            }

            /*LINQで指定した条件に合致するディレクトリ一覧wお列挙*/
            var di2 = new DirectoryInfo(@"/Users/xxx/Documents/");
            var directories2 = di.EnumerateDirectories().Where(x => x.Name.Length >= 10);
            foreach (var dinfo in directories2)
            {
                Console.WriteLine(dinfo.FullName);
            }

パスの操作

取得した絶対パスやファイル名、等を組み合わせたり、バラしたりすることも Pathを利用すればできます。

            var path = @"/Users/itoutomokazu/Documents/Testing/output2.txt";
            var directoryName = Path.GetDirectoryName(path); // ファイルのディレクトリ
            var filename = Path.GetFileName(path); // ファイル名(拡張子付き)
            var extension = Path.GetExtension(path); //ファイルの拡張子
            var filenameWithoutExtension = Path.GetFileNameWithoutExtension(path); //ファイル名(拡張子なし)
            var pathroot = Path.GetPathRoot(path); //ファイルがあるディレクトリのルート

            var path_combine = Path.Combine(directoryName, filename); //ディレクトリとファイル名を組み合わせて、絶対パスを作成(引数は何個でもいい)

アプリとDB以外でファイルでのやりとりが必要な場合に必要となってくる操作。 全部すぐには覚えられませんが、System.IOにFile、Directoryがあることを覚えておけば、 リファレンスをみてなんとか対処できるかな?

つぎは10章 正規表現。知らないと、暗号にしかみえないですからね〜

つづく

【C#】C#プログラミングのイディオム(8章)日付、時刻の操作

前回の続き

restofwaterimp.hatenablog.com

今回は日付、時刻。 よく使うのはDateTimeとstringの型変換と 差分を求めることかな。

私が使う時に、元号や曜日というのはあまり使わない。 もっぱら差分比較とか、n日前、n日後を使うことが多い。

     var dt1 = new DateTime(2016, 2, 4); //時分秒は0:00:00
            var dt2 = new DateTime(2019, 3, 2, 12, 22, 11);

            Console.WriteLine(dt1 +":"+ dt2);

            /*getしかプロパティに設定されていない*/
            Console.WriteLine(DateTime.Today); //時分秒は0:00:00
            Console.WriteLine(DateTime.Now);   //時分秒まで設定

            Console.WriteLine(DateTime.Today.DayOfWeek);


            /*日付を文字列に変換*/
            Console.WriteLine(dt1.ToString("d"));

            /*比較は演算子*/
            if(dt1 < dt2){
                Console.WriteLine("dt2の方が新しい日にち");
            }

            /*DateTime型は不変オブジェクトだが、初期設定は可能。差分は新たな変数へ*/
            var future = dt2 + new TimeSpan(2, 30, 0); //TimeSpanで時分秒を加減できる
            Console.WriteLine(future);

            var nextDay = dt2.AddDays(2); //Addxxxで加減したい単位を指定可能
            Console.WriteLine(nextDay);

日付型の表示変換はdocsに書いてある。

docs.microsoft.com

月末も昔、cobolとかやっていた時はうるう年考えて計算していたり、 最近でも、求めたい月の翌月の1日 - 1をして求めていたような。 そんなことしなくても

     var today = DateTime.Today;
            int day = DateTime.DaysInMonth(today.Year, today.Month);
            Console.WriteLine("今月は{0}日ある", day);

DaysInMonthメソッドを使えば、指定した年月の日数を求めてくれるので、月末日を求められます。

8.5 日時の計算(応用)

には、DayOfWeek列挙型を利用し、曜日から日付を求めるテクニックが記載されている。

DayOfWeekを利用することで、次のx曜日の日付を求めることができる。

       /*DayOfWee.xx で曜日ごとに値を持っている。列挙型で、日曜日から0,1,2,3,と始まる*/
            var days = (int)DayOfWeek.Monday - (int)(today.DayOfWeek);
            if (days <= 0) days += 7;
            Console.WriteLine(today.AddDays(days));

この章の最後に「プログラミングには唯一の正解はない」と描かれているが、その通りと思う。 機能として動けば、読みやすい・わかりやすいコードも、わかりにくいコードも同じ。 しかし、後々のことを考えると、わかりやすいコードでかつ、効率的、速度があるコードへ 徐々に書き換えた方がよい。

そこらへんはこの書籍だけでなく、他の良書に当たって、対応するのがよいかと。

次の章からは実践編 9章からは外部ファイルへのアクセス絡みに。

つづく

【C#】C#プログラミングのイディオム(7章)ディクショナリの操作

前回の話

restofwaterimp.hatenablog.com

基礎編もあと2章。次はディクショナリ。 業務アプリでは、データベースより取得した値や、内部でkey-valueで処理した方が良い時に利用しています。

ただ、あまり詳細なことを知らずに使っていた部分もあったので、書籍を読みながら振り返ります。

本章の内容 * Dictionaryの基本操作 * ディクショナリの応用 * サンプル(Abbreviationsクラス)

docs.microsoft.com

この章の前半はMicrosoftのリファレンスなどを参考するとわかりやすい。

            /* pre C# 5.0 */
            var flowerDict = new Dictionary<string, int>(){
                {"sunflower", 400},
                {"pansy", 300},
                {"tulip", 250},
                {"rose", 500},
                {"dahlia", 450},
            };

            /* after C# 6.0 */
            var flowerDict2 = new Dictionary<string, int>(){
                ["sunflower"] =  400,
                ["pansy"]= 300,
                ["tulip"]= 250,
                ["rose"]= 500,
                ["dahlia"]= 450,
            };

            // Add Dictionary Plus
            flowerDict.Add("himawari", 400);

            /*KeyとValueで分けないと、Dictionaryの値がそのまま出力される*/
            foreach (var s in flowerDict)
            {
                Console.WriteLine(s);
            }
            /*Keyとvalueに分ければ、それぞれの値を利用可能*/
            foreach (KeyValuePair<string, int> item in flowerDict)
            {
                Console.WriteLine("{0} = {1}", item.Key, item.Value);
            }

            flowerDict2["piti"] = 600;

            /*指定したキーの値がないものをそのまま利用すると、KeyNotFoundExceptionが発生*/
            Console.WriteLine(flowerDict["xx"]);

            /*回避するには、ContainsKeyを利用し存在チェックをするか、TryでExceptionを捕まえる*/
            /*指定したキーに該当する値があるか*/
            Console.WriteLine(flowerDict.ContainsKey("xx"));

通常のaddやremoveも利用しますが、Exceptionを発生させない方法を覚えておく。

タイプ keyの重複 格納する値
List 可能 なんでもOK
HashSet 不可 key
Dictionary 不可 key,value

Dictioonaryもkeyの重複はNGだが、valueのところをListやカスタムクラスを利用し、 keyに対するvalueを複数持たせることも可能で、 value に List型を指定すれば良い。

Listなら dict["xxx"].Add(value); と値を設定可能。

DictionaryのvalueにDictionaryを使う、多重構造もかけると思いますが、 記述すると複雑に・・・。 それよか、valueにクラスなどカスタマイズしたものを配置してコントロールした方が よさげです。

この章はざっと書いてありましたが、コーディングして身につけるのが一番の近道かと思います。

次は8章の時刻か・・・

つづく

【C#】C#プログラミングのイディオム(6章)配列とList<T>の操作

前回の続き

restofwaterimp.hatenablog.com

配列に対してはLINQを利用することが一番多いと思います。 LINQ以前はfor文やforeachで回しながら、該当の値を探したりしていたのですが、LINQを利用するとすっきり求めることができます。 利用の仕方を知った後に、利用していないソースを見ると「もったいないな〜」と思うようになれると思います。

テーマはこちら

  • 要素の設定
  • コレクションの集計
  • コレクションの判定
  • 単一要素の取得
  • 複数要素の取得
  • その他の操作

要素の設定

連続した文字列を設定する。 LINQならば

//List
Enumerable.Repeat("設定したい値","設定したい数").ToList();
//配列
Enumerable.Repeat("設定したい値","設定したい数").ToArray();

LINQを使わないならば、forとかforeachかな。(でも書籍では非推奨と書いてある) Pythonだと、[num] * count でできるから楽チン

//連続した数値の場合 Enumerable.range(開始の値、作成数).ToArray();  こっちのほうが便利

コレクションの集計

LINQのメソッドで直感的に求めることができる。

//平均
var avarage = numbers.Avarage();
var avarage = books.Avarage(x => x.Price);
//合計
var sum = numbers.Sum();
var sum = books.Sum(x => x.Price);
//最小値
var min = numbers.Min();
var min = numbers.Max();

どれも、LINQのメソッドで指定できる。条件を細かくしたい場合は、whereを利用すれば、条件指定できます。 LINQを利用しない場合、for や foreachで繰り返しながら、if文などのロジックを書きます。

コレクションの判定

該当のデータがあるか、ないかのチェックを行います。 LINQを利用すると便利。 SQLっぽく使えるのがよい。

//条件にあう要素があるか
bool exists = books.Any(x => x.Price < 1000);
//全ての要素が条件に該当するか
bool exists = books.All(x => x.Price < 1000);

単一要素の取得

アプリを作る時によく使うのがこの関連。 比較よりも該当するデータを取得する機能の方がよく使います。

/*Firstも最初の一件を取得だが、見つからないと、InvalidOperationExceptionが発生する。
 値が見つからない場合にDefaultの値を戻すこれらのメソッドを必ず使う*/
//最初の1件だけ
var text = books.FirstOrDefault(x => x.Price == 1000);
//最後の1件だけ
var text = books.LastOrDefault(x => x.Price == 1000);
//最初の位置を求める ⬅︎ これはメソッドの方がいいようです。確かに、LINQで取得は見たことがない。。。
var index = words.FindIndex(x => x < 0);

複数要素の取得

条件で指定するのはwhereメソッドを利用します。 書籍に書いてあるTakeメソッドは使ったことなかった。該当件数のみほしいとかなかったので。 取得した後、for文で回すよりはTakeの方がいいかも

//5より大きいのを最大3つ取得。3つなくても、取得した数だけ戻す。
var result = numbers.where(x => x > 5)
                                   .Take(3);

条件分岐のwhileに相当する TakeWhileやSkipWhileも記載がありますが、あまり使わないなー。

その他の処理

他にも単一化のDistinct()やOrderByなど、SQLぽいメソッドがたくさんあり、使えると便利です。 本書のColumnに記載の「要素の削除」については、そうだよね。と納得する部分でした。

リストや配列の中から、一部のデータを除外する時にforを利用しようとする人を見たことがあります。 forないでインプットにしている要素を削除すると、forの添え字が崩れ、正しくループしなかったり、 InvalidOperationExceptionが発生します。 そのため、forを利用するのではなく、 * 既存の要素をremoveAllで削除 * 取得したい値だけど別の変数に移動する のどちらかを利用して対処するのが良いです。

この章も知っていると便利というものが多かったです。 C#を使うならLINQをしっかり覚えよう!といった感じの章でした。

つづく

【C#】C#プログラミングのイディオム(5章) 文字列の操作

restofwaterimp.hatenablog.com

の続き

次は文字列の操作。 知っているか知っていないかでコーディングできるできないが結構別れる。 書籍に記載のように見やすくもかけるし、ゴリゴリもかけるので、できればすっきり見やすく書けるようになりたいです。

記載されているテーマは以下でした。

  • 文字列の比較
  • 文字の判定
  • 文字列の検索と抽出
  • 文字列の変換
  • 文字列の連結と分割
  • その他文字列操作

今回も気になった部分をピックアップします。

文字列の比較

よく入門時に、オブジェクトが同じものかどうかと中身の値が同じかどうかの比較で頭を悩ます部分がありましたが、 この本の説明は「値」の比較ということで説明されています。

比較 書き方 備考欄
単純比較 str1 == str2
大文字小文字区別無し String.Compare(str1, str2 , ignoreCase : true) ignoreCaseの書き方は C# 4.0以降 trueだけでもかける
ひらがなカタカナ区別無し String.Compare(str1, str2 , new CultureInfo('target language'), CompareOptions.IgnoreKanaType) ignoreCaseの書き方は C# 4.0以降 trueだけでもかける
全角半角区別無し String.Compare(str1, str2, new CultureInfo('target language') , CompareOptions.IgnoreWidth)

先にUpperCaseとか使って、無理やり大文字にとかにして、比較していたな〜と。

MSDNのサイトに書く説明の記載がありました。

String.Compare メソッド (System)

文字列の判定

MSDNのStringメソッドを見ても、多い!と覚えきれない。ただ、「こんなことできないかな?」と思った時に確認はしたいですね。

String メソッド (System)

普段よく使うのは

String.IsNullOrEmpty(str)
str.Contains("strにあるか調べたい文字列")
str.Contains('strにあるか調べたい文字')

文字列の検索と抽出

文字列の抽出が使うことが多い str.Substring(startIndex, cut_length) ただ、こういう時に記載する開始位置にマジックナンバーとして、ハードコーディングはして欲しくないですね。 先にconstでもいいから固定値で定義しておいて、その変数名から設定している意味を表せるようにするとよいです。

文字列のメソッドはそのまま出力とかするのならば、str.xxxでよいが、 結果を使いまわすというときは別の変数に値をおく必要があります。 当たり前じゃん、なんだけど、初学者のうちはやってなくて、値が取れないという人を見かけるので。

文字列の連結と分割

単純には var name = "str1" + "str2" と + 演算子です

メソッドを利用するなら String.Join(separator, array)  ・・ separatorに指定した文字で、文字をつける

str.split(separator) ・・・ separatorsに指定した文字で、文字列を分割し、配列型にする

stringなど文字列は不変オブジェクト そのため、仮に str = str + "aaa"; としても、左辺のstrと右辺のstrは別のメモリ番地を使っている。 やりすぎると、それだけでメモリを多めに使ってしまう。

代わりにStringBuilderを利用することで、解消できます。 var sb = new StringBuilder(); sb.Append("hoge") とするとsbで使用しているメモリ番地の値に"hoge"を追加します。

書籍に記載の指針は ### 繰り返し処理をしない場合は + 演算子 ### foreachなどで、文字列連結を繰り返す場合、StringBuilder ### 繰り返す回数が少ない場合は + 演算子 を使う

その他の文字列操作

pythonなどと同じく、文字列で定義されたものは

foreach(var c in str){
  Console.WriteLine(c + ',');
}

とすれば、strに設定した文字を一つずつ出せる。

文字列へ変換

 hoge.ToString();

とすれば、hogのものを文字列に置き換えられる。 また、ToString('format') またはstring.format(フォーマットの型、hoge) で書式変換できる。

c# 6.0以降で利用できるものが増えてもいる。 せっかく便利なメソッドやLINQでの指定ができるので、使わない手はない。

つづく・・・

【C#】C#プログラミングのイディオム(4章) 基本イディオム

 

restofwaterimp.hatenablog.com

 

の続きです。

 

3章までは準備編となっていて、4章からは基本イディオムの紹介

仕事で使っているのが、C# 5.0(Visual Studio 2013)なのと、今までC#のバージョンを折ったことなかったので、C# 6.0以降で利用できるイディオムを初めて知った。

知っているのと知らないのでは、自分のコーディングのスピード、メンテナンス性と他人のソースをレビューする速度や品質が変わるので、使える部分は使いたいと思う。

 

記載されている基本イディオムの内容は以下のものでした。

  • 初期化
  • 分岐、比較
  • 繰り返し
  • 条件演算子、null演算子
  • プロパティ
  • メソッド
  • その他

可読性や保守性を考えた場合、こう書いた方がいいよねということ記載されています。

気になった部分や「そうなんだ」と思った箇所を中心に書きます。

初期化

配列の初期化

  var names = new List<string> { "Tokyo", "Nagoya", "kyoto", };

 最後の項目の後ろにカンマをつけておく。

 なんで、最後にカンマつけるのだろうとよく思っていた。

 実際、カンマはつけなくてもコンパイルは通ります。

 他の記事とか見ていますと、項目とカンマをセットにして値を取り出したいとか

 した場合、ついていた方がいいよね、と記載されていました。

 配列というかenumで定義する時に最後にカンマつけるているケースを見かけます

 

オブジェクトの初期化

    public class Person{
        public string Name { get; set; }
        public string Birthday { get; set; }
        public string PhoneNumber { get; set; }
        }

 

    var person = new Person
    {
       Name = "hogehoge",
       Birthday = "20010910",
       PhoneNumber = "xxx",
        };

 

オブジェクトを初期化するときはこう書くけど、入門書や少し古い本だと

  person.Name = "hogehoge";

と書いてあるもんもありますよね。

 

書籍に書いてある通り、初期化コードの間に、無関係なロジック入れる人がごくごくたまにいるので、。

 

分岐、比較

returnの扱い方

時々、「1メソッドにreturnは一つしか書いてはダメ」みたいな教えを受けた人に出会い、途中でreturn を書けば、見やすいのに。

 if ( xxx ) {

   if ( xxx) {

       if(xxx) {

      }

   }

 }

 return  value;

みたいな感じのifのネストもしくはフラグ使いまくり・・・のソースに出くわします。

意味合いが同じなら、途中でreturn を書いて、ソースを見やすくしてほしいな。

 

繰り返し

要素の数だけ繰り返す

 

        public void a(){
            var nums = new List<int> { 1, 2, 3, 4, 5, };
            nums.ForEach(n => Console.WriteLine(n));
        }

LINQのForEachメソッドを利用するとこうかけるみたいです。

いつもforeach(var n in xxx) を使っているので、そういう書き方するんだという印象。

1文だけのそy理なら、ForEachメソッドで書くといいねと書いてありました。

 

条件演算子、null演算子

条件演算子

条件が複雑でない、戻り値が単純なら以下のように記述する。

var value = xxxx ? trueの値 : falseの値

条件演算子は見た目がすっきり!になりますが、条件が複雑であったり、戻り値に対する処理や値が多い場合はif文を使ってもらった方が、後々意味が通る。

 

合体演算子

.net 2.0で導入されているが、この書き方で描かれているのはお目にかかったことがない。

Null合体演算子 - Wikipedia

 

var message = hoge(code) ?? Default-value

 

null演算子

c# 6.0 から導入されている。c#5.0までしか見たことないので、お初。

value?.hoge

hogeの値がない場合nullで返してくれる。

if(value == null){

  return null;

}else{

  return value.hoge

}

 

と同等

 

プロパティ

 

読み取り専用プロパティ

プロパティのsetアクセサーをprivateの可視性にして定義するか、

setアクセサーをそもそも記述しないことで対応できる。

C# 6.0からは

public string Name => hoge + "" + xxx;

とすることで、読み取り専用のプロパティを作成できる。

 

参照型の読み取り専用プロパティ

上記のようにprivateで書いても、書き換えられないのは参照(メモリの)のみで、

参照先の値は変更できます。そのため、こちらが意図したい、値の変更を不可ということが実現できません。

 

コレクション自体を変更不可にするにはIReadOnlyList<type>,IEnumerable<type>を利用する。

public IReadOnlyList<int> hogeList { get; private set;}

 

メソッドに関する

可変長引数

params キーワード

ログの引数を与えたりする時に使ってます。

public void WriteLog(string messageFormat, params object[] args){

   var s = String.Format(messageFormat, args);

   WriteLine(s);

}

 

呼び出し元は

 hoge.WriteLog("output format", time, user, action);

         time,user,action など、個数に限らず、paramsを指定することで可変に設定可能

 

オーバーロードよりオプション引数

 多態性オーバーロードを見ることはありますが、C#に限らず、使われ始めているような。

    method(int num, string message = "hogehoge", int count = 3);

  呼び元

  numは必ず指定する。他は指定していなければ、デフォルト値が設定されます。

  結果、オーバーロードっぽく見えます。

  method(3);

       method(4, "hello");

       method(6, "good", 3);

その他

逐語的リテラル文字

 先頭に@を書きます。

 var path = @"C:¥resource¥file.txt";

    @を書くことで¥がエスケープ文字として認識されなくなります。

 固定値で書くときはこれでいいのですが、データベースなどで設定している値をシステム、特にOSウィンドウの値などに設定すると、エスケース文字として¥が扱われることがあります。

 

 その場合、かっこ悪いですが

  '"' + path + '"' と切って、エスケープ文字として見られないように

  前後にDouble Quotationを入れたりしています。

 

文字列を数値に変換

int.Parse(str)を利用すると、失敗した時にExceptionが発生して面倒なんですよ。

本書に記載のように先に失敗しないかをint.TryParse(str)で確かめておくと確かにいい感じです。

 

例外の再スロー

 throw;だけです。

時々 throw ex;で書かれます。スタックトレースが消えるのでご勘弁・・です。

 

 

他にも書いてありますが、そこは書籍で確認で!

 

標準化という訳ではないですが、「こうやって常にコードを書く」と意識しておくことで、綺麗で、あとで見やすいコードになります。

 

次は5章 文字列の操作

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774187585/restofwaterim-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51Q1LFkj2qL._SL160_.jpg" alt="実戦で役立つ C#プログラミングのイディオム/定石&パターン" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774187585/restofwaterim-22/ref=nosim/" name="amazletlink" target="_blank">実戦で役立つ C#プログラミングのイディオム/定石&パターン</a><div class="amazlet-powered-date" style="font-size:80%;margin-top:5px;line-height:120%">posted with <a href="http://www.amazlet.com/" title="amazlet" target="_blank">amazlet</a> at 18.09.09</div></div><div class="amazlet-detail">出井 秀行 <br />技術評論社 <br />売り上げランキング: 10,714<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774187585/restofwaterim-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>

【C#】C#プログラミングのイディオム(3章) ラムダ式とLINQの基礎と1章(プログラミングの基礎)

図書館で本を借りて、

 

3章はラムダ式LINQについて。

 

何気に、LINQを使っていたが、言語の進化の歴史については知らなかったので、そうだったんだ!という感じです。

 

この章ではラムダ式以前と以後について説明されております。

経緯は知らなかったので、メモメモ・・

 

1. メソッドに普通に引数を渡す

// 配列を引数に受け取るCountメソッド
class Step02 {
  public void Do() {
    var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
    var count = Count(numbers, 5);
    Console.WriteLine(count);
  }
 
  public int Count(int numbers, int num) {
    int count = 0;
    foreach (var n in numbers) {
      if (n == num)
        count++;
       }
      return count;
  }

}

 2. デリゲートによる実現

class Step04 {
public delegate bool Judgement(int value); //delegateの指定

 
public void Do() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, IsEven);
Console.WriteLine(count);
}
 
public bool IsEven(int n) {
return n % 2 == 0;
}

public int Count(int numbers, Judgement judge) {
int count = 0;
foreach (var n in numbers) {
if (judge(n) == true)
count++;
}
return count;
}
}

 

3. 匿名メソッドの利用

class Step05 {
public int Count(int numbers, Predicate<int> judge) {
int count = 0;
foreach (var n in numbers) {
if (judge(n) == true)
count++;
}
return count;
}
 
public void Do() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, delegate (int n) { return n % 2 == 0; });
Console.WriteLine(count);
}
}

 

4. ラムダ式

 

3でいう、Countメソッドを次のように変えることができます。

4-1 まずは、そのままべたに書く

public void Step0() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
Predicate<int> judge =
(int n) => {
if (n % 2 == 0)
return true;
else
return false;
};
var count = Count(numbers, judge);
Console.WriteLine(count);
}
 
4-2.変数judgeを利用せず、そのままCountの引数に設定する

public void Step1() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers,
(int n) => {
if (n % 2 == 0)
return true;
else
return false;
}
);
Console.WriteLine(count);
}
 
4-3.if文の戻り値がboolなので、簡略化することができる

public void Step2() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, (int n) => { return n % 2 == 0; });
Console.WriteLine(count);

}
 
4-4.ラムダ式の{}内が1文の場合、{}とreturnが省略可能

public void Step3() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, (int n) => n % 2 == 0);
Console.WriteLine(count);

}
 
4-5.ラムダ式は引数の型を省略できる

public void Step4() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, (n) => n % 2 == 0);
Console.WriteLine(count);

}
 
4-6.ラムダ式は引数が1つの場合、()を省略できる

public void Step5() {
var numbers = new { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };
var count = Count(numbers, n => n % 2 == 0);
Console.WriteLine(count);
}

 

 

ラムダ式でかけるようになってから、delegateや匿名メソッドを活用して、「どうやって書こう」から、ラムダ式ですっきりかけるので、「何を書こう」に変わりました。

 

確かに、ラムダ式便利なんですよね。これあが実現されたLINQ to Objectを利用すれば、変数に対して、SQLっぽく扱えるので。以前みたいに、ゴリゴリコードをかかなくてよくなりましたから。

 

LINQ to Objectsの使い方

・System.Collections.Generic

・System.Linq

名前空間を指定します。

 

例えば。

 

   var names = new List<string>{
       "America", "Brazil", "Italia", "China", "Japan"
       };

   IEnumerable<string> query = names.Where(x => x.Length <= 5);

 

クエリの実行 | Microsoft Docs

 

利用する際、リンク演算子が「即時実行」なのか「遅延実行」なのかを知っておく必要があります。

演算子を定義した時の値で評価するのか、演算子を利用する場合の値で評価するのかの違いです。

 

即時実行したいのに、遅延実行で記載していると、取得したい値が取得できなかったりします。

 

---------------------------------------------

2020/10/24 追加  1章の話

 

 class と struct

 

クラスは参照型、structは値型となる。

参照型はヒープ領域、値型はスタック領域に値が保持される

 

私自身、structを使っているプログラムをほとんど見たことがない。

メモリを効率的に使うということで、小さい値ならstructを利用するほうがいい。

大きい値を利用する倍、ポインタで参照を利用するclassをとなるが・・・。

書籍に記載の通り、structは見たことないんですよね。使っているのを。

 

ポインタとメモリと型(構造体)の関係 (2) - C言語 - 碧色工房

 

 

 

▪️学習している書籍