restofwaterimpのぎじゅつMemo

SIerに所属してます。企画から運用までやってます。現状、プロジェクトをスクラムで回したい!と試行錯誤中です

【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 でできるから楽チン

コレクションの集計

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の基礎

図書館で本を借りて、

 

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

 

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

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

 

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

 

 

 

▪️学習している書籍 

【振り返り】Redmineをソフトウェア開発に導入して2年使ってみて(初期の頃)

今のお客さんの開発に携わり、割とプロジェクト管理に関するルールなども自社ルールに縛られず、お客さんのルールにも縛られずに入れる環境であったので、Redmine入れて管理したいなと思い導入してから約2年。

 

やってみて、個人的に便利 or あと少し・・・ということを勝手に振り返ります。

ただ、結論的に言えることは「Excel」管理やMSProjectには戻れないなと。

自分がやりやすいやり方に今は行き着いているので、これが正解と捉えず、こういう例もあるんだと捉えていただければ幸いです。

(スクラムっぽくプロジェクトもスプリントも設けて実施していましたが、ここではRedmineの使い方のみに焦点を当てています。)

 

1.導入した経緯

これまで進捗の管理にはMS Excelを利用してくる場面が多かった。なんでプロジェクト管理の手法が旧態依然のまま誰もが、それに従っていたのかよくわからず、「ルール」という縛りにあがらうことできず過ごしてました。

そこで、まあある意味実験的にいろいろと試すことができる環境に置かれたものだから、「アジャイル」「Lean」というバズワード(まあ、2015年ごろ入れたので、もう普通に使われてますね)に引かれ、「まずは使ってみよう」と自分のチームとお客さんに宣言して利用し始めました。

※かんばんを壁に紙で貼るということも考えましたが、お客さん先で複数ベンダーが入っている関係上、セキュリティ上NGでしたので、Redmineを利用しました。

 

2.初期のころ

Redmineの入門本を購入し、デフォルトの機能のみで管理してました。

(今考えると無鉄砲で計画性がなかったなと反省・・・)

 

Redmine超入門 増補改訂版 (日経BPムック)
飯田 治行 大澤 文孝 大和田 裕 小川 明彦 楠川 智久 阪井 誠 内藤 淳
日経BP社 (2015-06-30)
売り上げランキング: 345,042
この本をみながらBitnamiでPCにインストールし、Redmine運用。
(この当時はVersion 2.6を入れていたはず)
 
この時点で実施していたのは、RedmineSVN(tortoisesvn)で、
SVNRedmineは連係してませんでした。。(知らなかった)
また、全体を通してですが、うちのチームの初期の力ではTDDができなかったので、テスト方法はテストコードによるテストではなく、静的コードチェックと画面を利用したテストが中心です。
 

どう管理していたの??

「トラッカー」で作業を分類してました
良かった点1 
チケット登録するとき、トラッカー別に「設計」「実装」「テスト」「レビュー」と分けて登録できるので、登録は楽でした。
ある設計のチケットに対し、「実装」トラッカーを子チケットに。
ある「実装」チケットに対し、「テスト」「レビュー」のトラッカーを子チケットに。
 
先にWBSのようにチケットを登録し、ガントチャートやチケット一覧をみながらやれるのかなと思っていましたが・・・。
 
良かった点2
優先度をつけられるのはメンバにとって良かったらしいです。
どれから着手すべきか口頭で伝えられても忘れてしまうので、書かれているといいらしい。
同じように期日を切ってくれているとメンバ自身でスケジュールコントロールするときの一つの目安になっていいみたい。
 
問題点1 階層が深いとガントチャート以外で関連がわからない
 
階層を深くするとチケット一覧を確認しても「???」となりよくわからない。
進捗もよくわからないし、チケット数の管理が3桁越えると何が何だかわからず。
当時MAXで500くらいのチケットがアクティブで登録されていました。
やらなければならないタスクをとりあえず書いたのですが、「開始予定日」「完了予定日」でしっかりコントロールしないと「未着手」で遅延なのか、「まだやる必要ない」のかをチケット一つずつ内容をみないと行けなかったので、余計な管理工数がかかっていました。
 
 
問題点2 チケットの移動に制約が・・・
 
親子関係、関連付けをチケットにつけていると「ここからここにこのチケットは移動しよう」と思って動かしても、制約違反にかかる。。。移動できないじゃんということが発生。自由にチケット移動できず、どうしましょ、という状態でした。
 
問題点3 要件との紐付けをしていなかったので、まとまりがわからず
 
バグ管理や課題管理で利用している場合には作成したトラッカーに対して、それをこなせば、TODOとしては終わりですが、ソフトウェア開発となるとそうはいかず。
設計チケットをトップに記載したため、「あれ、この設計チケットってどの要件の話だっけ」とか設計チケット通しに関連性をつけていなかったので「このチケット直すとどこに影響するのか?」というのが第三者がみてわからなかった。
完全にチケット管理する前のワークフローなり、トラッカーの付け方の計画ミスでした。
 
どう解決したの?
 
プラグインというのを入れるのにこのころは抵抗があり(挙動を自分で管理できなかった)、カスタムフィールド等で解決してました。
 
解決策1 親子関係はつけない。カスタムフィールドで管理
 
とりあえず、親子のチケット関係は外しました。代わりにカスタムフィールドで属性を追加しました。
例えば、「トラッカー」でコントロールしていた内容を「作業内容」というカスタムフィールドを作成し、この列挙項目に「設計」「実装」「テスト」などを追加。
また、要件と紐づけるために「画面」とか「要件」というカスタムフィールドを作成し、列挙項目に要件名や画面ID等を描き、このカスタムフィールドでまとめられるようにしました。
 
解決策2 題名でも何しているかわかるように
 
解決策1では管理して、データを収集、集計するためには便利ですが、日々、メンバがチケットを確認する上では「わざわざチケットを開かないとなんの作業かわからない」という問題がありました。そこで、ありきたりですがチケットの題名に【設計作業】や【実装】など接頭に作業内容をつけてなんのチケットを書くかわかるようにしました。
 
 

ここまでで良くなったの?

この管理でかつ、途中でRedmineSVN連係も追加し、約1年はこの設定状態で管理をしました。

手持ちのチケット数が少ないとメンバも余裕があることと、自分自身のスケジュール管理もできていたのですが、追加・変更案件とテストによる指摘事項がチケットに混在してくると「どのチケットからやるの」「優先順位をどうつけるの」というのが錯綜し、若干パニック状態になりました。

 

また、内部管理はチケットやRedmineの内容で説明すればある程度状況の把握はできていましたが、第三者や上位の管理者に説明するときにスケジュールを数字化するのに一苦労しました。

チケット数の総数、消化数、残数。予定時間と実績時間から想定される予想残工数。バグ発生の予想数、消化予想工数から着地がどれくらいになるのか・・・。チケットから作成するのにデータを抽出し、こねこねしなければならず、プロジェクト管理が楽になったのか?というのが若干疑問でした。

 

しかし、とは言っても、Redmineに記載された内容がトレースできていたので、「書かれてさえいれば」何に困っていたのか。何がうまく言っていたのかを分析するのには大変助かりました。

 

 

つづく