WPF IValueConverter小メモ
DateTime向けのコンバーターでDateTime以外の入力があったら例外投げていたけど、
どうもnullで飛んでくるタイミングがあって例外発生してたもよう。
変換に失敗したら例外投げるより既定値で渡した方が良い?
そのうち検証しよう。
インターフェースを考える(3)IPredicate
今回もC#実践開発手法から、IPredicateを題材にします。
まずはインターフェース
namespace Nmdm.Predicates { public interface IPredicate { bool Test(); } }
ITaskインターフェース同様引数はありませんが、戻り値がbool型です。
戻り値をジェネリックにしてIFunc
条件判定という目的に特化している。と考えたら別のインターフェースにした方がシンプルかな。
ITaskの時と同じようにモックを作るのですが、bool型を返すのでtrueを返すモックとfalseを返すモックの2パターンを作ります。
まずは常にtrueを返すTruePredicateクラス
namespace Nmdm.Predicates { public sealed class TruePredicate : IPredicate { public bool Test() { return true; } } }
反対に常にfalseを返すFalsePredicateクラス
namespace Nmdm.Predicates { public sealed class FalsePredicate : IPredicate { public bool Test() { return false; } } }
IPredicateを組み立てるためのクラスですが、ここでは一旦置いておき、
ITaskとの連携を考えてみます。
まずはTrueかFalseか判定して処理を分岐させるクラスとしてTestedTaskを作ってみました。
C#実践開発手法の中で紹介されている分岐デコレータと同じですね。
using Nmdm.Tasks; namespace Nmdm.Predicates { public sealed class TestedTask : ITask { public TestedTask(IPredicate predicate,ITask trueTask,ITask falseTask) { this.Predicate = predicate; this.TrueTask = trueTask; this.FalseTask = falseTask; } private IPredicate Predicate { get; } private ITask TrueTask { get; } private ITask FalseTask { get; } public void Do() { if(this.Predicate.Test()) { this.TrueTask.Do(); return; } this.FalseTask.Do(); } } }
条件判定と処理を組み合わせて処理の分岐を表現しているわけですね。なるほど・・・
記事が長くなりそうなのでこのへんで一端切ります。
インターフェースを考える(2)ITaskインターフェース
C#実践開発手法で紹介されているもっとも単純なインターフェースITaskを作ってみます。
namespace Nmdm.Tasks { public interface ITask { void Do(); } }
引数も無く、戻り値もなく、最もシンプルなインターフェースですね。
こういうインターフェースを作るとテストの時のモックが必要になるだろう。
というわけでITaskを実装したNullオブジェクト、NullTaskを作りました。
namespace Nmdm.Tasks { public sealed class NullTask : ITask { public void Do() { } } }
じゃあ今度はITaskを組み合わせて連続でDoメソッドを実行出来るクラスCompositeTaskクラスを作ってみます。
using System; using System.Collections.Generic; namespace Nmdm.Tasks { public sealed class CompositeTask:ITask { public CompositeTask(IEnumerable<ITask> tasks) { if (tasks == null) throw new ArgumentNullException("tasks"); this.TaskList.AddRange(tasks); this.TaskList.RemoveAll(x => x == null); } private List<ITask> TaskList { get; } = new List<ITask>(); public void Do() { this.TaskList.ForEach(x => x.Do()); } } }
nullチェックのタイミングとかnullを無視する処理の書き方はともかく、
コンストラクタでIEnumerable
条件判定も何もないから、Compositeタスクさえあれば組み合わせるのには十分なような気がするけど、
書きながら思いついたのはTry-CatchやTry-Fillalyを仕込みたい時はどうするんだろう?という話
例えばDBへの書き込み処理なんかだとアプリケーション側で
try { Hoge.BeginTransaction(); // 永続化ロジックはここ Hoge.Commit(); } finally { Hoge.RollBack(); }
みたいな具合の処理を書きたくなる。
Begin⇒永続化⇒Commitの流れはCompositeTaskに分解したタスクを渡せば連続で実行出来るけどRollBackはそうはいかんですね。
トランザクション処理に特化したクラスを作るのであればITaskを実装したTransactionクラス的なのにITaskを実装した永続化ロジックを渡してやれば実現出来るでしょうけど、
あえてITaskだけに依存した方法で書くことを考えてみましょう。
まずはcatchを無視して、tryブロックとfinallyブロックに分けてITaskを実行するTryFinallyTaskを組んでみます。
using System; namespace Nmdm.Tasks { public sealed class TryFinallyTask : ITask { public TryFinallyTask(ITask tryTask,ITask finallyTask) { if (tryTask == null) throw new ArgumentNullException("tryTask"); if (tryTask == null) throw new ArgumentNullException("finallyTask"); this.TryTask = tryTask; this.FinallyTask = finallyTask; } private ITask TryTask { get; } private ITask FinallyTask { get; } public void Do() { try { this.TryTask.Do(); } finally { this.FinallyTask.Do(); } } } }
というわけで実装してみました。
コンストラクタでtryブロックで実行する処理とfinallyブロックで実行する処理を受け取りプロパティに格納する。
Doメソッドでtry-finallyを使ってそれぞれのITaskを実行すると・・・
もう一つのtry-catchの方はどうなるだろう。
通常try-catchでやることと言えば例外をキャッチしてログを出力したり、
ファイルIOなんかの外部リソース周りの例外をキャッチして自作のクラスで利用者に通知を出したりかなぁ、
単にExceptionをキャッチするだけじゃなく具体的な例外をキャッチすることを考えるとこのインターフェースで表現出来る???
とりあえずITaskについて考えるのはこのくらいにしときます。
うーん、プログラムは小さくてシンプルな方が良いとは思うけど、やりすぎ感がプンプンしますねー。
インターフェースを考える(1)はじめに
オブジェクト指向プログラミングを勉強していると
実装ではなく抽象に依存する
というフレーズを目にすることがあります。
このカテゴリの記事では私自身の勉強のために、
とことんインターフェースを使ってみたらどうなるか、
実際にプログラミングしながら考察していこうかと思います。
c# リファクタリング 小さい方の値が欲しい。
二つのint型から小さい方の値がほしい時。
public void Hoge(int one,int other) { int min; if (one < other) { min = one; } else { min = other; } }
じゃなくて。MathクラスのMinメソッドを使おう。
public void Hoge(int one,int other) { int min; min = Math.Min(one, other); }
MSDNで見ればわかることですが、longやbyte等、大体の型のオーバーロードが用意されています。
大きい方の値であればMath.Max
https://msdn.microsoft.com/ja-jp/library/system.math.max(v=vs.110).aspx
DateTime構造体にもこういうのないものかなぁと思ったらStackOverFlowでも似たような話がありました。
残念な英語力なので雰囲気で提案されている解決方法を真似してみます。
まずは三項演算子
DateTime min; min = one < other ? one : other;
Ticksプロパティ(long型)をMathクラスに渡す方法
DateTime min;
min = new DateTime(Math.Min(one.Ticks, other.Ticks));
Utilityクラス見たいなので再利用する。
public static class DateTimeUtil { public static DateTime Min(DateTime one,DateTime other) { return new DateTime(Math.Min(one.Ticks, other.Ticks)); } public static DateTime Max(DateTime one, DateTime other) { return new DateTime(Math.Max(one.Ticks, other.Ticks)); } }
最後に挙げた安易にユーティリティクラスを作ることの是非は別として、
TicksをMathクラスに渡すのはへぇ~ってなりました。
C# 自作クラスの戻り値からnullを排除する。
自作クラスのプロパティやメソッドでなんらかの値を返すならnullは返さないようにしたい。
そのクラス自体のnullチェックを他のクラスがするのはわかるとして、
クラスが公開する「何か」についてはそのクラスが責任を持つべき。
string型のプロパティがあるならnullの代わりにstring.Emptyを返せばいいし。
他のクラスを返すならnullObjectパターンを使えばいい。
コレクションなら空のコレクションを返す。
C# リファクタリング コレクションを変換
コレクションの内容を一定のルールに従って違う型に変換したい場合
public void Hoge() { var list = new List<object>(); list.Add(new object()); list.Add(new object()); list.Add(new object()); list.Add(new object()); list.Add(new object()); var list2 = new List<int>(); foreach(var obj in list) { list2.Add(obj.GetHashCode()); } }
foreachを取り除くには
public void Hoge() { var list = new List<object>(); list.Add(new object()); list.Add(new object()); list.Add(new object()); list.Add(new object()); list.Add(new object()); var list2 = new List<int>(); list2.AddRange(list.ConvertAll<int>(x => x.GetHashCode())); }
ConvertAllメソッドを使ってやる。