http://eventdots.jp/event/592870
「WPFからみるMVVM」楽しみです。
]]>あとFriendlyを使って、プロダクトでは絶対やってはいけないような事を率先してやってます。
テストプログラミングって自由だな〜!Friendly最高だな〜!素敵すぎますね!
あと、最近ようやくAWSやAzureといったクラウド関連に触手が伸ばせるようになりました。
サーバレスアーキテクチャ!なんだろそれ!うまいだろきっと!
AWS LambdaやAzure Functionおぼえよ!って鼻息荒くしてます!面白そうだ〜。
]]>
評価:
市川 忠男 森北出版 ¥ 3,672 (2005-03) コメント:4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。 |
評価:
増永 良文 サイエンス社 ¥ 2,808 (2003-03) コメント:特にデータベース管理者の心意気とか余計な主張とかはなく、純粋にリーレーショナルデータベースに関して論理的な展開で紹介されている良本だと思いました。教え方がうまいと思うし、論理的だと思う。すばらしいです。 |
評価:
奥野 幹也 技術評論社 ¥ 3,110 (2015-03-10) コメント:難しい |
日記です。
SQLがわからん。
DBがわからん。
DBAコマンド全く覚えられない。
という状態のまま、10年くらいPGやってます。あな恥ずかしです。
チューニングがどうのこうのと言われましても・・・。赤面です。
まあ既に死亡確定みたいな状態ではあるのですが、
生き恥さらして、ちへどをはきながら、底辺を這いつくばって苦手克服ですよ。
というわけで、買ってみました。読んでみました。丁度一ヶ月前くらい?
「理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL (WEB+DB PRESS plus)」
すみません。手元に今ないので不正確なのですが、
2章だったか3章だったか、数式が出てきまして、
これがサッパリでして、見たことある記号に見えるけど、
なんだっけ?調べてみると、自分が知ってる記号と何か違ったり
いよいよ混乱の谷底へとおこちちゃいました。
今回の自分は、そこで終わりません。
じゃー数学勉強すればいいじゃない!?
と、方向転換
「ろんりと集合」
命題論理の演算
反射法則、交換法則、ド・モルガンなどの法則
述語論理
集合
集合の法則と論理の法則の対応
写像
などなど一度は勉強したことがあるようなないような内容でしたが
とてもわかりやすい本だったので、わかったような実感を得ることができました。
ただ、「理論から学ぶデータベース実践入門」を読むための数学の知識は
もう少し高度なものが要求されているような気がします。
どうしたら良いんだろう・・・。
ちなみに記号は色々と流派があるようでして、
「記号論理入門 (日評数学選書)」
この本の付録Aを読むと、ある程度の対応付けが可能になるかな・・・。
データベースの論理は
データベース実践講義 ―エンジニアのためのリレーショナル理論 (THEORY/IN/PRACTICE)
プログラマのためのSQL 第4版 すべてを知り尽くしたいあなたに
あたりに詳しく書いているかもしれない・・・読めてないからわからない。
評価:
Alexander A. Stepanov,Daniel E. Rose 翔泳社 ¥ 2,808 (2015-05-19) |
問1 以下、のGOFデザインパターンを簡単に説明せよ。
Abstract Factory
Adapter
Bridge
Builder
Chain of Responsibility
Command
Composite
Decorator
Facade
Factory Method
Flyweight
Interpreter
Iterator
Mediator
Memento
Observer
Prototype
Proxy
Singleton
State
Strategy
Template Method
Visitor
回答
Abstract Factory
具体的な生成は具象クラスが提供するが、利用者は共通のインスタンス生成を保証するパターン。
Adapter
異なる型同士を中継するアダプターを提供するパターン。
Bridge
特定のロジックだけを抜き出し、再利用するパターン。
Builder
複数の型が関連する複雑なインスタンス生成するパターン。
Chain of Responsibility
状態をリポジトリに保存・取得するパターン。
Command
複数のロジックを共通の実行形式にするパターン。
Composite
再帰的な構造をとるパターン。
Decorator
インスタンスを特定の状態にするパターン。
Facade
システムの呼び出し口を一つにするパターン。
Factory Method
・・・。いつも忘れる。
Flyweight
なんだっけ(笑)
Interpreter
こんなのあったけ(笑)
Iterator
コレクションの列挙する構造を提供するパターン。
Mediator
こんなのあったけ(笑)
Memento
思い出せない・・・。
Observer
状態の変更を通知する構造を持つパターン。
Prototype
うん・・・。
Proxy
Adapterとほぼ同じ・・・提供する側かされる側が用意するのかで名前が変わったような。
Singleton
インスタンスが一つであることを保証するパターン。
State
あれ?StateとStrategyの片方しか思い出せない。
Strategy
状態により振る舞いを変えるパターン。
Template Method
特定の振る舞いを抽象化する構造を持つパターン。
Visitor
ロジックと状態を分離する構造を持つパターン。
正解(Wikiを参考にしました。http://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2))
Abstract Factory
関連する一連のインスタンスを状況に応じて適切に生成する方法を提供する。
Adapter
元々関連性のない2つのクラスを接続するクラスを作る。
Bridge
クラスなどの実装と、呼出し側の間の橋渡しをするクラスを用意し、実装を隠蔽する。
Builder
複合化されたインスタンスの生成過程を隠蔽する。
Chain of Responsibility
イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されてゆくようにする。
Command
複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで操作の切替えを実現する。
Composite
再帰的な構造を表現する。
Decorator
あるインスタンスに対し、動的に付加機能を追加する。Filterとも呼ばれる。
Facade
複数のサブシステムの窓口となる共通のインタフェースを提供する。
Factory Method
実際に生成されるインスタンスに依存しない、インスタンスの生成方法を提供する。
Flyweight
多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす。
Interpreter
構文解析のために、文法規則を反映するクラス構造を作る。
Iterator
複数の要素を内包するオブジェクトのすべての要素に順にアクセスする方法を提供する。反復子。
Mediator
オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする。
Memento
データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする。
Observer (出版-購読型モデル)
インスタンスの変化を他のインスタンスから監視できるようにする。Listenerとも呼ばれる。
Prototype
同様のインスタンスを生成するために、原型のインスタンスを複製する。
Proxy
共通のインタフェースをもつインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる。
Singleton
あるクラスについて、インスタンスが単一であることを保証する。
State
オブジェクトの状態を変化させることで、処理内容を変えられるようにする。
Strategy
データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切替えを容易にする。
Template Method
あるアルゴリズムの途中経過で必要な処理を抽象メソッドに委ね、その実装を変えることで処理が変えられるようにする。
Visitor
データ構造を保持するクラスと、それに対して処理を行うクラスを分離する。
コメント
う〜ん。大分、間違えてます。うん、ブログに載せるの恥ずかしいけど、これから再勉強して、マスターするどー。
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace Art55.TfsApi20140508_001
{
class Program
{
static void Main(string[] args)
{
var teamCollectionUri = new Uri(@"https://XXXXXXXX.visualstudio.com/DefaultCollection");
var teamCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(teamCollectionUri);
// 前に認証されていない場合は Team Foundation Server につながりを認証します。
teamCollection.EnsureAuthenticated();
var versionControlService = teamCollection.GetService<VersionControlServer>();
const string localProjectPath = @"c:/source/test";
Workspace localWorkspace = versionControlService.TryGetWorkspace(localProjectPath);
if (localWorkspace != null)
{
// すでに存在する場合は、削除する。
localWorkspace.Delete();
}
string workspaceName = string.Format("{0}_{1}", Environment.MachineName, DateTime.Now.ToString("yyyyMMddHHmmssms"));
const string serverProjectPath = @"$/hoge/";
localWorkspace = versionControlService
.CreateWorkspace(
workspaceName,
versionControlService.AuthorizedUser,
string.Empty,
new[] { new WorkingFolder(serverProjectPath, localProjectPath) });
localWorkspace.Get();
localWorkspace.Delete();
}
}
}
ちょっとだけ説明。
var teamCollectionUri = new Uri(@"https://XXXXXXXX.visualstudio.com/DefaultCollection");
var teamCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(teamCollectionUri);
自分のvisual Stuido OnlineのURI(つまりURLです。)とチームコレクションを指定します。URIだけ指定して、後かチームコレクションにアクセスする2段構えの方法もあります。今回は、チームコレクションを直接指定しました。
const string localProjectPath = @"c:/source/test";
ローカルのワークスペースのパスを指定します。c:/source/testというパスを指定しています。一時的に利用するなら、時刻などを指定したフォルダを作成してもいいかもしれません。サンプルコードなので適当ですが、実際に利用しているWorkSpaceのパスなどを指定すると、ギャーってなるので注意してください。
string workspaceName = string.Format("{0}_{1}", Environment.MachineName, DateTime.Now.ToString("yyyyMMddHHmmssms"));
ワークスペース名です。今回は、既存のワークスペースは利用せず一時的に別のワークスペースを作成し、そこにソースを落としたいので、既存のワークスペースに被らないような名前を指定しました。ワークスペースが被らないことも注意する必要がありますが、その前に指定したlocalProjectPath も既存のワークスペースですでに指定されていたりするとエラーとなるので注意が必要です。
localWorkspace = versionControlService
.CreateWorkspace(
workspaceName,
versionControlService.AuthorizedUser,
string.Empty,
new[] { new WorkingFolder(serverProjectPath, localProjectPath) });
ワークスペースを作成します。既存のワークスペースを取得する場合は、GetWorkspaceメソッドまたあTryGetWorkspaceメソッドを利用します。
const string serverProjectPath = @"$/hoge/";
Visaul Studio Onlineの取得したいソースのパスを指定します。
localWorkspace.Get();
実際に、ソースを取得します。
localWorkspace.Delete();
一時的に利用したかっただけなのでワークスペースを削除します。削除し忘れるとVisual Studio 2013等から削除しなければなりません。
今回は、ユーザ名、ドメイン、パスワードを指定していませんが、これはVisual Studio 2013からVisual Stuido Onlineへ接続している為と思われます。未接続の場合は、認証する処理も必要です。
まとめ
1.Visual Studio Onlineは、ローカル上のTFSと同様にTFS APIが利用できる。
2.TFS APIは、以下のdllを参照することで利用できる。
・Microsoft.TeamFoundation.Client.dll
・Microsoft.TeamFoundation.VersionControl.Client.dll
3.TFS APIを利用することで、Visual Studio 2013からソース管理するよりも高度な操作が可能である。
------------------------------------------
【キーボード】6年前のRealForceを復活させることはできる!?その3
http://pro.art55.jp/?eid=1304218
【キーボード】6年前のRealForceを復活させることはできる!?その1
http://pro.art55.jp/?eid=1304216
【キーボード】6年前のRealForceを復活させることはできる!?その2
http://pro.art55.jp/?eid=1304217
------------------------------------------
評価:
--- アイリスオーヤマ(IRIS) ¥ 554 (2008-12-10) |
開封する。
机に設置する。
キーボードが滑らないか押してみる。
滑らない。
キーボードがガタつかないか右上を押してみる。
ガタガタ
あれ?
あー爪が中途半端になってるんだな。
爪を見てみるとたたまれている。
うん?
もしかして、爪を出した状態でがたつかないようにしたのかな?
なんて思ってみる。
爪を出してみて
がたつきチェック。
ガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタガタ
おおおおおお!
がたつくぞ!
てか前の10倍くらいガタつくぞ!
その後、ガタガタ震えながら営業さんの携帯に電話しました。
電話した結果新品を送りますって事になりました。
どーなっとんじゃ!!!
ちなみにどれくらい隙間が空いてたかというと、
一円玉が差し込めるくらい隙間空いてましたわ。
報告によると、キーボード内部の鉄板の爪の部分が反り返っていた為
ガタつきがあったという事でしたが、
もしかして、マジで力任せにねじったのかな?
金属って一度ゆがむともとには戻らないよ?素人なん?
力任せにねじると、数日間は金属が動くよ。うん。素人なん?まじで!?
東プレの中に人にキーボードクラッシャーがいるとは思わなかったよ。
----------------------------------------
(追記)
そういえば、RealForceを注文したあと、待ち焦がれすぎて、いろいろしてべていた時期に一つ発見したことを思い出しました。たしかゆがんでいるって内容が2チャンネルのまとめブログ(だとおもう)に掲載されていて、内容をおっていくと筐体をねじると治るみたいな指摘を受け、実際に不具合を訴えてた人が試したら治ったみたいな投稿があったことを思い出しました。もしかして、ねじると治るのかな?って思うんですけど、交換してくれると約束してくれたので、まあ、やめておきます。はい。てか、中の人、まさかねじっただけ?ねじると時間がたつと、またずれるよね・・・なんども微調整が必要だよね・・・めんどくさー。
----------------------------------------
(追記)
東プレさんから連絡がありました。現在、在庫がなく、次回の製造が完成次第交換していただけるとのことでした。それからガタつくキーボードをその間つかわなければならないので対応策も教えてもらいました。
「ちょっと乱暴ですが、KBを手に持った状態で
左側を下方向、右側を上方向にねじってください」
※これやって壊れても、私は責任を負いかねますので、
同じ症状で悩んでる方は素直に東プレさんに相談されることをお勧めします。
クラス設計から見たDataContextプロパティ
DataContextプロパティは、FrameworkElementクラスで定義されているプロパティ
DataContextプロパティは、依存関係プロパティ。
DataContextプロパティは、FrameworkPropertyMetadataOptions.Inheritsオプションが指定されている。
参考資料
FrameworkElement.DataContext プロパティ
http://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.datacontext(v=vs.110).aspx
------------------
(追記)
「ItemsSourceの型」に関してちょいと意味不明なのでした。
この前、東プレのRealForce UBK-Sを買ったじゃん!って言われそうですが、ダイヤテック FilcoのMajestouchも気になっていたので、気になっていたので、2回言いましたよ。買っちゃいました。にわかキーボードマニアです。にわかです。はい。で、買ったのは
「Majestouch 2 Tenkeyless クリームホワイト・茶軸・かなあり」というやつです。うん。見た目がきれい。春って感じです。
きれいでしょ?見つけたら買うでしょ?
で、使ってみて、うん。あれです。茶軸って、Cherry軸の中ではうるさくない方だと思ってたんですが、「中では」とは、比較するところ間違ってましたね。キーボードの中ではうるさいって言うかやかましい部類でした。お店とかで茶軸を散々試し打ちしてたんですが、お店ってそもそもやかましいじゃないですか・・・家の中とは環境が違いますよね。気づかなかったー。お店だとRealForceなんて静音じゃなくて無音になってたもんなー。と、まあ、一人暮らしなので、家でやかましくけたたましく打とうが、そこまで気にする必要はないのですが、会社は無理かなー。会社で二つ必要なんだけどなー。うーん、どうしよう。
まあ、いいんです。美しいから、
ほら、Caps Lockをかけるとグリーンに光る。美しいです。Caps LockもScroll Lockも全く使わないんですが・・・。うーん。
そうそう、これってクリームホワイト×グリーンなんですが、クリームホワイト×ピンクとかもいけるんじゃないかなーって思うんです。今のところリリース予定はなさそうですが、女性ウケが良いかとおもうんですよね。まーキーボードにこだわる女性がどのくらいいるのか知りませんが、新しい客層の開拓とかしないのかなー?
ためしにPhotoShopでピンクにしてみました。
ちょっとピンクがくすんでしまってしまいましたが、うん。やはりいけると思います!
---------------------------------------------
真面目な話
今回、紹介したキーボードは
Majestouch 2 Tenkeyless のWeb限定発売の特別カラー仕様です。おそらくですが、色以外は「Majestouch 2 Tenkeyless 」と全く同じものと思われます。Cherry軸で赤軸と茶軸のみあります。玄人好みの黒軸と青軸はないようです。キートップは、かなありの日本語配列です。10キーがありません。エンターキーとスペースキーは、納品時は白になっていますが、上記写真の通り、緑色のキーが用意されており、取り換えが可能です。ただし、エンターキーとスペースキーも針金のサスペンサーがついており、取り換えにはちょいとテクニックが必要です。まあ、とにかくお洒落です。普段、タイピングされている方で、音を気にされない方にプレゼントとかいかがでしょうか?うん、これは素直にお洒落だと思います。
【通販限定】Majestouch 2 Tenkeyless クリームホワイト・茶軸・かなあり
【通販限定】Majestouch 2 Tenkeyless クリームホワイト・赤軸・かなあり
--------------------------------------------
(追記 2014/03/29)
会社等の開けた場所で利用してみましたが、そこまで気になる音ではありませんでした。
逆に狭い部屋では、音が響くためうるさく感じたようです。
評価:
--- FILCO ¥ 9,545 (2011-06-09) |
評価:
--- 東プレ ¥ 18,907 (2010-08-05) |
<Window x:Class="Art55.DataGridComboBoxDemo20140311_01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:demo="clr-namespace:Art55.DataGridComboBoxDemo20140311_01"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainWindowViewModelKey}">
<Window.Resources>
<demo:FromTextToItemsSourceConvter x:Key="FromTextToItemsSourceConvterKey" />
<demo:FromItemsSourceToSelectedIndexConverter x:Key="FromItemsSourceToSelectedIndexConverterKey" />
<Style TargetType="ComboBox" x:Key="EditingElementStyleKey">
<Setter Property="IsEditable" Value="True" />
</Style>
<Style TargetType="ComboBox" x:Key="ElementStyleKey">
<Setter Property="ItemsSource"
Value="{Binding Path=Text,
RelativeSource={RelativeSource Self},
Converter={StaticResource FromTextToItemsSourceConvterKey}}" />
<Setter Property="SelectedIndex"
Value="{Binding Path=ItemsSource,
RelativeSource={RelativeSource Self},
Converter={StaticResource FromItemsSourceToSelectedIndexConverterKey},
Mode=OneWay}"></Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid CanUserAddRows="False"
CanUserDeleteRows="False"
AutoGenerateColumns="False"
DataContext="{Binding Path=Source}"
ItemsSource="{Binding .}">
<DataGrid.Columns>
<DataGridTextColumn Header="No" Binding="{Binding No}"></DataGridTextColumn>
<DataGridComboBoxColumn Header="ComboBox Column"
TextBinding="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
EditingElementStyle="{StaticResource EditingElementStyleKey}"
ElementStyle="{StaticResource ElementStyleKey}">
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Command="{Binding RejectChangedAllRowsCommand}">変更取消</Button>
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using Art55.DataGridComboBoxDemo20140311_01.Annotations;
namespace Art55.DataGridComboBoxDemo20140311_01
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
_source = new DataTable {CaseSensitive = true};
_source.Columns.Add("No", typeof(int));
_source.Columns.Add("Value");
_source.Rows.Add(new object[] { 1, DBNull.Value });
_source.Rows.Add(new object[] { 2, "A2" });
_source.Rows.Add(new object[] { 3, "A3" });
_source.Rows.Add(new object[] { 4, DBNull.Value });
_source.Rows.Add(new object[] { 5, DBNull.Value });
_source.Rows.Add(new object[] { 6, "A6" });
_source.Rows.Add(new object[] { 7, "A7" });
_source.Rows.Add(new object[] { 8, DBNull.Value });
_source.AcceptChanges();
RejectChangedAllRowsCommand = new CommandObject(this,
_ => _source.RejectChanges());
}
public ICommand RejectChangedAllRowsCommand { get; private set; }
// TODO コマンドを用意してDataTableを操作してみると面白いよ。
private readonly DataTable _source;
public DataTable Source
{
get { return _source; }
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class FromTextToItemsSourceConvter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return DependencyProperty.UnsetValue;
}
return new[] {value};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class FromItemsSourceToSelectedIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var items = value as object[];
return items == null ? -1 : 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class CommandObject : ICommand
{
public CommandObject(INotifyPropertyChanged viewModel, Action<object> execute)
: this(viewModel, execute, null)
{
}
public CommandObject(INotifyPropertyChanged viewModel, Action<object> execute, Func<object, bool> canExecute)
{
if (viewModel == null)
throw new ArgumentNullException("viewModel");
if (execute == null)
throw new ArgumentNullException("execute");
viewModel.PropertyChanged += OnViewModelPropertyChanged;
_execute = execute;
_canExecute = canExecute;
}
void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
CheckCanExecute();
}
public void CheckCanExecute()
{
InvokeCanExecuteChanged(new EventArgs());
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute.Invoke(parameter);
return true;
}
public event EventHandler CanExecuteChanged;
private void InvokeCanExecuteChanged(EventArgs e)
{
EventHandler changed = CanExecuteChanged;
if (changed != null)
changed(this, e);
}
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
}
}
ポイントは、赤字で書いた部分です。
<Style TargetType="ComboBox" x:Key="EditingElementStyleKey">
<Setter Property="IsEditable" Value="True" />
</Style>
<Style TargetType="ComboBox" x:Key="ElementStyleKey">
<Setter Property="ItemsSource"
Value="{Binding Path=Text,
RelativeSource={RelativeSource Self},
Converter={StaticResource FromTextToItemsSourceConvterKey}}" />
<Setter Property="SelectedIndex"
Value="{Binding Path=ItemsSource,
RelativeSource={RelativeSource Self},
Converter={StaticResource FromItemsSourceToSelectedIndexConverterKey},
Mode=OneWay}"></Setter>
</Style>
1.編集中は、ComboBox.IsEditableにTrueを指定する。
2.参照中は、ComboBox.Textの変更を監視し、変更のあったタイミングでItemsSourceに入力文字を設定する。
3.参照中は、ComboBox.ItemsSourceを監視し、ComboBox.ItemsSourceの数に応じてSelectedIndexを設定する。
監視対処が
Text ← ItemsSource ← SelectedIndex
と、XAMLで定義しました。以前書いたコードは、DataRowの特定のカラム値やDataRowViewの変更を監視していたりと、タイミングに不整合が起きていたのですが、今回は、それを解消してみました。
手入力可能なComboBoxを表示させる方法はいくつかあります。前に書いた方法としてDataGridTemplateColumnを利用するというのも一つの手です。WpfToolkit時代はDataGridComboBoxColumnは編集時はComboBox、参照時はTextBlockを利用していたと記憶していますが、DataGridTemplateColumnを利用すれば、近いものが実装できると思います。
それと、今回の実装では、まだ機能的にはお粗末です。すぐにわかるのが、一つは入力開始制御がF2とダブルクリック以外でできていない点です。ほかにもあるかもしれません。という事で、ないと思いますが、このコードを利用する場合は注意してください。
Source and Project
<Window x:Class="Art55.EditableComboBoxDataGrid20140308_02.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:editableComboBoxDataGrid2014030802="clr-namespace:Art55.EditableComboBoxDataGrid20140308_02"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<editableComboBoxDataGrid2014030802:TextBlockComboBoxItemsSourceConverter x:Key="TextBlockComboBoxItemsSourceConverterKey" />
<!-- 編集モード時のComboBox -->
<Style TargetType="ComboBox" x:Key="EditingElementStyle">
<Setter Property="IsEditable" Value="True" />
</Style>
<!-- 参照モード時のComboBox -->
<Style TargetType="ComboBox" x:Key="TextBlockComboBoxStyle">
<!--<Setter Property="Text" Value="{Binding Column1}" />-->
<Setter Property="ItemsSource"
Value="{Binding .,
Converter={StaticResource TextBlockComboBoxItemsSourceConverterKey},
ConverterParameter=Column1}" />
</Style>
<!-- 既定のDataGridのスタイル -->
<Style TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid x:Name="SampleDataGrid">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Column1"
TextBinding="{Binding Column1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
EditingElementStyle="{StaticResource EditingElementStyle}"
ElementStyle="{StaticResource TextBlockComboBoxStyle}"
/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Click="OnSelectedRowsRejectChanges">選択行の値を元に戻す。</Button>
</Grid>
</Window>
<!-- 参照モード時のComboBox -->
<Style TargetType="ComboBox" x:Key="TextBlockComboBoxStyle">
<Setter Property="ItemsSource"
Value="{Binding .,
Converter={StaticResource TextBlockComboBoxItemsSourceConverterKey},
ConverterParameter=Column1}" />
</Style>
using System.Data;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace Art55.DataGridCell20140305_001
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataTable source = CreateSampleData();
dataGrid.ItemsSource = source.DefaultView;
}
private void OnClickick(object sender, RoutedEventArgs e)
{
var dataGridCellInfo = new DataGridCellInfo(dataGrid.Items[40], dataGrid.Columns[2]);
dataGrid.CurrentCell = dataGridCellInfo;
dataGrid.ScrollIntoView(dataGridCellInfo.Item);
DoEvents();
FrameworkElement contentElement = dataGridCellInfo.Column.GetCellContent(dataGridCellInfo.Item);
if (contentElement == null)
{
return;
}
var dataGridCell = contentElement.Parent as DataGridCell;
if (dataGridCell == null)
{
return;
}
dataGridCell.Focus();
// IsEditingを利用した場合、コンテンツを表示しているコントロールにフォーカスが当たらないなどの諸問題がある。
// dataGridCell.IsEditing = true;
// DataGrid.BeginEditCommand.Execute(null, dataGridCell);
dataGrid.BeginEdit();
}
private static void DoEvents()
{
// NOTE: http://msdn.microsoft.com/ja-jp/library/system.windows.threading.dispatcher.pushframe(VS.80).aspx
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrames), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrames(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
private static DataTable CreateSampleData()
{
var source = new DataTable();
source.Columns.Add("Column1");
source.Columns.Add("Column2");
source.Columns.Add("Column3");
source.Columns.Add("Column4");
Enumerable.Range(0, 100)
.GroupBy(n => Enumerable
.Range(0, source.Columns.Count)
.Select(m => ((char)('A' + m))
.ToString(CultureInfo.InvariantCulture) + n)
.Cast<object>()
.ToArray()
, (n, m) => n)
.ToList()
.ForEach(item => source.Rows.Add(item));
source.AcceptChanges();
return source;
}
}
}
namespace Art55.DataGridCell20140305_001
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataTable source = CreateSampleData();
dataGrid.ItemsSource = source.DefaultView;
}
private void OnClickick(object sender, RoutedEventArgs e)
{
var dataGridCellInfo = new DataGridCellInfo(dataGrid.Items[40], dataGrid.Columns[2]);
dataGrid.ScrollIntoView(dataGridCellInfo.Item);
DoEvents();
FrameworkElement contentElement = dataGridCellInfo.Column.GetCellContent(dataGridCellInfo.Item);
if (contentElement == null)
{
return;
}
var dataGridCell = contentElement.Parent as DataGridCell;
if (dataGridCell == null)
{
return;
}
dataGridCell.Focus();
}
private static void DoEvents()
{
// NOTE: http://msdn.microsoft.com/ja-jp/library/system.windows.threading.dispatcher.pushframe(VS.80).aspx
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrames), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrames(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
private static DataTable CreateSampleData()
{
var source = new DataTable();
source.Columns.Add("Column1");
source.Columns.Add("Column2");
source.Columns.Add("Column3");
source.Columns.Add("Column4");
Enumerable.Range(0, 100)
.GroupBy(n => Enumerable
.Range(0, source.Columns.Count)
.Select(m => ((char)('A' + m))
.ToString(CultureInfo.InvariantCulture) + n)
.Cast<object>()
.ToArray()
, (n, m) => n)
.ToList()
.ForEach(item => source.Rows.Add(item));
source.AcceptChanges();
return source;
}
}
}
ポイントをまとめると
1.DataGridCell.Focus()を呼び出すことで、セルにフォーカスを当てることができる。
2.DataGridCellは仮想モードの場合、見えていないと存在しない可能性がある。つまり見せる必要がある。
以上
いろいろ試行錯誤したんですが、「いろいろ」なのでとりあえず、シンプルに書いたコードだけ紹介します。
<Window x:Class="ComboBoxDataGrid20140301_001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:ComboBoxDataGrid20140301_001"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- 既定のDataGridのスタイル -->
<Style TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid x:Name="SampleDataGrid">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Column1"
TextBinding="{Binding Column1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Click="OnContentChanged">メモリ上のデータを表示する。</Button>
</Grid>
</Window>
using System.Data;
using System.Linq;
using System.Text;
using System.Windows;
namespace ComboBoxDataGrid20140301_001
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var dataTable = new DataTable() { TableName = "SampleData" };
dataTable.Columns.Add("Column1");
dataTable.Columns.Add("Column2");
dataTable.Columns.Add("Column3");
Enumerable.Range(0, 10)
.Select(n => new object[] { "A" + n, "B" + n, "C" + n })
.ToList()
.ForEach(item => dataTable.Rows.Add(item));
SampleDataGrid.ItemsSource = dataTable.DefaultView;
}
private void OnContentChanged(object sender, RoutedEventArgs e)
{
string message = SampleDataGrid
.ItemsSource
.OfType<DataRowView>()
.Select(rowView => string.Join(", ", rowView.Row.ItemArray))
.Aggregate(new StringBuilder(), (sb, line) => sb.AppendLine(line))
.ToString();
MessageBox.Show(message);
}
}
}
上記のコードを実行してみると、要件を全然満たせていないことがわかります。
1.参照モードで値が表示されていない。
2.編集開始時に値が表示さていない。
3.編集中のComboBoxコントロールは手入力できない。
唯一編集中はセルがComboBoxになるという点だけ要件を満たせています。うん。どうしたものかといところです。この問題を解決するには、大きく分けて二つ手があると思います。DataGridComoBoxColumnのEditingElementStyleおよびElementStyleからStyleを変更し、要件を満たす。もう一つはDataGridComboBoxColumnの利用をやめて、DataGridTemplateColumnを利用する。今回はStyleを編集する方向で、実現することにしました。
変更したソースは以下です。
<Window x:Class="ComboBoxDataGrid20140301_001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:ComboBoxDataGrid20140301_001"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<y:TextBlockComboBoxItemsSourceConverter x:Key="TextBlockComboBoxItemsSourceConverterKey" />
<!-- 編集モード時のComboBox -->
<Style TargetType="ComboBox" x:Key="EditingElementStyle">
<Setter Property="IsEditable" Value="True" />
</Style>
<!-- 参照モード時のComboBox -->
<Style TargetType="ComboBox" x:Key="TextBlockComboBoxStyle">
<!--<Setter Property="Text" Value="{Binding Column1}" />-->
<Setter Property="ItemsSource"
Value="{Binding .,
Converter={StaticResource TextBlockComboBoxItemsSourceConverterKey},
ConverterParameter=Column1}" />
</Style>
<!-- 既定のDataGridのスタイル -->
<Style TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid x:Name="SampleDataGrid">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Column1"
TextBinding="{Binding Column1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
EditingElementStyle="{StaticResource EditingElementStyle}"
ElementStyle="{StaticResource TextBlockComboBoxStyle}"
/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Click="OnContentChanged">メモリ上のデータを表示する。</Button>
</Grid>
</Window>
using System;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
namespace ComboBoxDataGrid20140301_001
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var dataTable = new DataTable { TableName = "SampleData" };
dataTable.Columns.Add("Column1");
dataTable.Columns.Add("Column2");
dataTable.Columns.Add("Column3");
Enumerable.Range(0, 10)
.Select(n => new object[] { "A" + n, "B" + n, "C" + n })
.ToList()
.ForEach(item => dataTable.Rows.Add(item));
SampleDataGrid.ItemsSource = dataTable.DefaultView;
}
private void OnContentChanged(object sender, RoutedEventArgs e)
{
string message = SampleDataGrid
.ItemsSource
.OfType<DataRowView>()
.Select(rowView => string.Join(", ", rowView.Row.ItemArray))
.Aggregate(new StringBuilder(), (sb, line) => sb.AppendLine(line))
.ToString();
MessageBox.Show(message);
}
}
public class TextBlockComboBoxItemsSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string columnName = (parameter as string) ?? string.Empty;
var dataRowView = value as DataRowView;
if (dataRowView == null
|| dataRowView.Row == null
|| !dataRowView.Row.Table.Columns.Contains(columnName))
{
return DependencyProperty.UnsetValue;
}
return new[] { dataRowView.Row[columnName] };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
すべてのを説明するのは面倒なので、要点だけ書くと
1.ComboBoxを手入力可能な設定にした。IsEditable=True
2.参照モードのComboBoxに値を表示できる値は、候補の値に限られているため
参照モードのみ候補に値を一件仕込んだ。
<Setter Property="ItemsSource" ... />
上記のことをすれば、参照モードでは、値が表示され、編集中は手入力可能なComboBoxが表示されるようになります。補足としては、データソースにDataTableを利用しています。それぞれのカラムにプリミティブな値しか設定しませんが、データソース側に多少の努力を加えると、IValueConverterあたりの下りは不要になるかもしれません。この辺は開発・保守の観点から費用対効果で結論を出すべきところですかね。今回は、知ってる知識で最短で組める方法でがんばってみました。
今回はComboBoxに候補を出すということはしていませんが、これに関しても色々方法があり、満たしたい要件で実装方法、難易度(知ってれば難易度なんてあがりませんが)が上がります。
以上で、今回は以上で終わります。久々にBlogを書くこと書き方忘れますね。
-----------------------------
(追記 2014/03/05)
Styleに不要なSetterがあったため修正しました。
誤)
<Setter Property="Text" Value="{Binding Column1}" />
正)
<!--<Setter Property="Text" Value="{Binding Column1}" />-->
TextBindingで上書きされる項目なので指定しても無効となります。
貧乏が憎い。
]]>
評価:
Kevin Hazzard,Jason Bock アスキー・メディアワークス ¥ 3,360 (2013-08-29) |
評価:
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー 翔泳社 ¥ 3,780 (2013-07-23) |
評価:
Jeffrey Richter 日経BP社 ¥ 8,190 (2013-10-10) コメント:発売予定美 2013年10月10日 |
評価:
ジョー・セルコ,Joe Celko 翔泳社 ¥ 2,940 (2007-11-02) |
評価:
ミック 翔泳社 ¥ 1,974 (2010-06-29) |
static void Main(string[] args)
{
object o1 = new StringBuilder("A").ToString();
object o2 = new StringBuilder("A").ToString();
Console.WriteLine(o1 == o2);
Console.WriteLine(o1.Equals(o2));
}
上記のプログラムを実行すると、
false
false
になるものだと思い込んでいました。
false
true
になりますよね。System.Objectの定義を見て冷静に考えれば出せる答えなのに、まったく別の現象を論証する過程で、「勘違い」して、非論理的に思い込んでいました。思い込みって怖い!
public interface IReadOnlyList<out T> : IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; }
}
.NET Framework 4.5では上記のように定義されているので、
public interface IList<T> : IReadOnlyList<T>, ICollection<T>, IEnumerable<T>, IEnumerable
{
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
T this[int index] { get; set; }
}
継承関係にある二つのインターフェイスに同名のプロパティが定義されていて、とても気持ち悪い状態になり、コンパイル時に警告が出るようにはなります。ただし、このインターフェイスのみを考えるとコンパイルは通ります。問題なさそうです。ただし、実際にはIList<T>がIReadOnlyList<T>から派生すると悲劇が生まれるます。
ちょいと簡単にするために一つインターフェイスを定義してみます。
public interface IDemo
{
int Get();
void Set(int value);
}
これに対して実装クラスをあったとします。
public class DemoImpl : IDemo
{
private int _value;
int IDemo.Get()
{
return _value;
}
void IDemo.Set(int value)
{
_value = value;
}
}
この時点ではコンパイルは通っています。これに対して、IDemoにIReadOnlyDemoを追加してみます。
public interface IDemo : IReadOnlyDemo
{
int Get();
void Set(int value);
}
public interface IReadOnlyDemo
{
int Get();
}
なんと!コンパイルが通らなくなります(あたりまえですが)
これを.NET Frameworkに置き換えて話すと、.NET Framework 4.0までのIList<T>とList<T>がありました。.NET Framework 4.5(仮)でIList<T>をIReadOnlyList<T>から派生させました。List<T>に問題が発生するのでList<T>に修正を加えます。そこでは、.NET Frameworkのクラスライブリーの責任の範囲内なので、実装できます。しかし、IList<T>から独自に実装していたクラスがあった場合、.NET Framework 4.5にターゲットを変更した場合、とたんにコンパイルエラーになってしまいます。これは、いただけないです。拡張したつもりが、実際にはアセンブリ間では変更になってしまいます。
だったら、同時期にIList<T>とIReadOnlyList<T>が提供されていたら問題はなかったのか
?という疑問がわいてきます。が!ちょっと私わかりません。もう少し考えてから続きは書きたいと思います。
.NET Framework 4.5をターゲットとして開発していると、「あれ?」って思うことが多々あります。細かいところで改善が入っていることに気づかされることがあります。
【.NET Framework 4.5】IReadOnlyListとIReadOnlyDictionary
では、IReadOnlyList<T>とIReadOnlyDictionary<TKey, TValue>が追加されたことを、今更ながらに紹介してみましたが、今回はArraySegment<T>クラス。
ArraySegment<T>クラスは、前から存在していたクラスですが、使いどころがサッパリわからないクラスでした。配列の特定の位置から特定の位置までをセグメントとして配列をラップするクラスだったのですが、血迷ったことにIEnumerableを継承していないという状態でした。なので使うときは
var arrary = new[] {0, 1, 2, 3, 4, 5, 6, 7};
var arraryMid = new ArraySegment<int>(arrary, 2, 4);
for (int index = arraryMid.Offset; index <= arraryMid.Offset + arraryMid.Count - 1; index++)
{
Console.WriteLine(arraryMid.Array[index]);
}
かえってめんどくさい事うけあいなクラスだったのですが、.NET Framework4.5から変更があり
var arrary = new[] { 0, 1, 2, 3, 4, 5, 6, 7 };
var arraryMid = new ArraySegment<int>(arrary, 2, 4);
foreach (int i in arraryMid)
{
Console.WriteLine(i);
}
みたいな使い方ができるようになっています。
.NET Framework 4.5より前は、インターフェイスを何も継承していないクラスだったのですが、.NET Framework 4.5では
IList<T>, ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
と、色々継承していています。
おかげで
var array = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8};
var arrayMidle = new ArraySegment<int>(array, 2, 4);
Console.WriteLine(string.Join(" ", arrayMidle));
IList<int> list = arrayMidle;
list[2] = 0;
Console.WriteLine(string.Join(" ", array));
のようなコードを書いて実行してみると
2 3 4 5
0 1 2 3 0 5 6 7 8
ラップクラスであるArraySegmentを通して、包含されているであろう元の配列の値が書き換わった事が確認できます。これって使いどころ結構あるんじゃないかって思うんですよね。っていうか、それまでがやる気なさすぎな残念なクラスだったと言うべきか・・・。
です。おわり。
-----------------
(追記)
http://msdn.microsoft.com/ja-jp/magazine/jj133817.aspx
思いっきり紹介されてました。
情弱なのです。
■NuGetからPetaPocoをインストール
■app.config(またはweb.config ない場合は作成する)を書き換える(赤字部分を追記)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<connectionStrings>
<add
name="Art55OracleDb"
connectionString="Data Source=XE;User Id=art55;Password=art55"
providerName="Oracle.DataAccess.Client"/>
</connectionStrings>
</configuration>
■Database.ttを開き
前)
ConnectionStringName = "";
後)
ConnectionStringName = "Art55OracleDb";
■Database.ttを右クリックし「カスタムツール」を実行する。
上記の手順だけで、本来ならPocoはできるはずなのですが、作成されません。エラーを追っていくとおかしな部分があるので書き換えます。
■PetaPoco.Core.ttincludeのOracleSchemaReaderクラスを書き換える。(1139行
1308行
前)
const string TABLE_SQL=@"select TABLE_NAME from USER_TABLES";
後)
const string TABLE_SQL=
@"select a.TABLE_NAME, b.OWNER as TABLE_SCHEMA, 'Table' as TABLE_TYPE
from USER_TABLES a
inner join ALL_TABLES b on a.TABLE_NAME = b.TABLE_NAME
union
select c.VIEW_NAME as TABLE_NAME, d.OWNER as TABLE_SCHEMA, 'View' as TABLE_TYPE
from USER_VIEWS c
inner join ALL_VIEWS d on c.VIEW_NAME = d.VIEW_NAME";
※とりあえず、ここのSQLでPocoとして作成したいテーブルを取得するためのSQLを書けば良いようです。ポイントはテーブル名にTABLE_NAME、オーナーにTABLE_SCHEMA、Viewも含めたい場合はTABLE_TYPEというカラムにViewと記述すれば良いようです。
■Nullable対応が間違えているので修正
1215行
前)
col.IsNullable=rdr["IsNullable"].ToString()=="YES";
後)
col.IsNullable=rdr["IsNull
able"].ToString()=="Y";
ここまででPocoはとりあえず作れるはずです。ただし、Number型のNullableの対応がおかしいので、それも修正する必要があります。
■PetaPoco.cs内のあるGetConverterメソッドを書き換える。
2094行
前)
else if (!dstType.IsAssignableFrom(srcType))
{
converter = delegate(object src) { return Convert.ChangeType(src, dstType, null); };
}
後)
else if (!dstType.IsAssignableFrom(srcType))
{
Type innerType = Nullable.GetUnderlyingType(dstType);
if (innerType != null)
{
converter = src => Convert.ChangeType(src, innerType, CultureInfo.InvariantCulture);
}
else
{
converter = src => Convert.ChangeType(src, dstType, CultureInfo.InvariantCulture);
}
}
これで、とりあえず私が見つけた範囲のバグは修正されます。うん。全然だめっぽいという事だけわかりました・・・ただし、ソースコードとT4 Templateで配布されており一切のdllを含めていないため、自分のやりたい範囲で都合のいいように書き換えられるというのは、メリットだと思います。
最後に、とりあえず動かしてみたコード
using System;
using System.Globalization;
using System.Linq;
using Art55OracleDb;
namespace Art55.PetaPocoDemo20130206_001
{
class Program
{
static void Main()
{
using (var db = new PetaPoco.Database("Art55OracleDb"))
{
db
.Query<EMP>("select * from emp where job=@Job", new { Job = "SALESMAN" })
.ToList()
.ForEach(emp => Console.WriteLine(string.Join(", ", new []
{
emp.EMPNO.ToString(CultureInfo.InvariantCulture),
emp.ENAME,
emp.JOB
})));
}
}
}
}
実行結果
7499, ALLEN, SALESMAN
7521, WARD, SALESMAN
7654, MARTIN, SALESMAN
7844, TURNER, SALESMAN
まとめ
PetaPoco + Oracleは怪しい・・・。私の力量では手に負えないと思いました(汗)
Source and Project
「再入門 Linq to Objects 」はじめました。体系立てて理解するというのが目的です。誤解のないように説明を加えると、このブログで体系立てて説明する気はありません。体系立てて理解するのは私であって、現状は体系だてて説明できないのです。そういう事です。後は察していただければ幸いです。
今回は
System.Linq.Enumerableクラスに存在するpublicなメソッドの一覧を確認したいと思います。Linq to Objectでは、これらのメソッドを組み合わせてロジックを組んだりするので、とりあえず全部知っておけば、いいじゃないかというのが目論見です。
リフレクションを使って確認してみたところ、メソッド名は以下の通りありました。
Where
Select
SelectMany
Take
TakeWhile
Skip
SkipWhile
Join
GroupJoin
OrderBy
OrderByDescending
ThenBy
ThenByDescending
GroupBy
Concat
Zip
Distinct
Union
Intersect
Except
Reverse
SequenceEqual
AsEnumerable
ToArray
ToList
ToDictionary
ToLookup
DefaultIfEmpty
OfType
Cast
First
FirstOrDefault
Last
LastOrDefault
Single
SingleOrDefault
ElementAt
ElementAtOrDefault
Range
Repeat
Empty
Any
All
Count
LongCount
Contains
Aggregate
Sum
Min
Max
Average
「大体、一度は使ったことがあるなー」というのが感想です。恥ずかしながらJoinとGroupJoinは使ったことがありません。SequenceEqualとLongCountとContainsも使ったことがありません。
Conncatを使うべきところをUnionを使ってしまった事があるなーっとか、全て感想です。
今回はここまでです。次からは上から順番に見ていこうと思います。オーバーロードが沢山ありそうなので、大変な気がしてきました。うーん。
-----------------------
参考までにコードを載せておきます。
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication29
{
class Program
{
static void Main()
{
var enumerableType = typeof (Enumerable);
enumerableType
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Select(info => info.Name)
.Distinct()
.ToList()
.ForEach(Console.WriteLine);
}
}
}
残念ながら、このアルゴリズムは、いくつかの問題があります。ひとつは、異なるキーでハッシュコードが重複するという問題です。文字列など無限の組み合わせが存在する場合は、GetHashCodeメソッドの戻り値のint型で表せる範囲を超えることは自明です。
では、問題を解決してみることにします。
用意した配列に直接値を入れず、Keyと値を保持したオブジェクトの参照を配列に格納してみることにします。Keyと値を保持したオブジェクトに更に参照をつなげて線形リストにします。これでキーがぶつかった場合は、リストが連結されていき、検索する際は、ハッシュコードとまず一致する事で線形リストのヘッダーにたどり着き、Keyの等価性を見ることで、重複するハッシュコードでも検索が可能になります。
しかし、まだ、問題があります。図中では「ハッシュテーブル」と名付けている配列の要素数が問題となります。仮にGetHashCodeメソッドが返す範囲をカバーする配列を作ろうとしても作れません。まずはsz配列を利用したいため、負の値は問題となります。System.Int32.MinValueが0に対応するように計算させると、今度は最大値はintの範囲を超える値にもなります。なので負の数字は扱えません。さらに、System.Int32.MaxValueの要素数を持つ配列のインスタンスは生成できません。
var arrary = new int[System.Int32.MaxValue];
上記のコードはコンパイルは通りますが、実行すると例外が発生します。メモリが確保できないからです。リソース上の問題なので回避できません。
そこで、この問題を解決するために、GetHashCodeメソッドをから得たハッシュコードを更に圧縮する必要があります。そして、圧縮した範囲をカバーできる配列を用意するというアルゴリズムにします。前回の考察で「要素数は素数で、要素数を超える場合は、リサイズされる。要素数(素数)を2倍した値から順方向に存在する素数のメンバーのうち、最初の見つかる素数を次の要素数にする」という動きをいたと思いますが、とりあえず、要素数が7の場合で考えてみたいと思います。
上記の図は、ハッシュテーブルの要素が7の場合です。本来ならキーは4つ以上登録ずみですが、ごちゃごちゃするので省略しています。まず、AddしたタイミングでKeyからGetHashCodeメソッドを呼び出し、ハッシュコードを得ます。その値を要素数で割り余りを得ることでハッシュコードを0から6の範囲で圧縮することができます。その値がハッシュテーブルのindexに対応していることがわかります。参照をたぐる事で、検索時にキーから値を連想することが可能です。今回は、Keyで比較するよりも圧縮前のハッシュコードで比較した方が高速であるため、ハッシュテーブルの参照先のオブジェクトにハッシュコードをキャッシュさせました。
これで、ディクショナリの概念的なアルゴリズムは説明できていると思います。ただ、System.Collections.Generic.Dictionary<T, TKey>の内部状態の観察から、上記の説明で「参照」と書いた部分は、実際には「参照」ではなく、配列のインデックスを利用しているという事が分かっているため、そこを少しだけ書き換えてみることにします。
絵は大幅に変わりましたが、線形リストの実装方法を配列に切り替えただけです。
キー1から値を検索する際は、キー1のハッシュコードを圧縮し、ハッシュテーブルのインデックスに対応させ、その値からエントリーの配列のインデックスを見つけ、エントリー内の配列からキー1と一致する要素を見つけ、最終的に「メロン」という値を見つけ出すことができる。という事がわかります。
と、まあこんな感じでアルゴリズムを考える上での「トンチ絵」が書けました。・・・いつの間にか絵を描くことが目的になってしまいましたが、私の頭はすっかりすっきりになったので、今回はこれで終了。