加速度センサーとInvokeについての覚書
<はじめに>
WP7で加速度センサーを使ってみようと思います。Accelerometerのインスタンスを生成すると、CurrentValueChangedイベントとReadingChangedイベントがあるのですが、「ReadingChangedは古いよ!」って注意されるのでCurrentValueChangedにて実装します。
<プログラム>
public partial class MainPage : PhoneApplicationPage { private Accelerometer acceleroter; /// <summary> /// コンストラクタ /// </summary> public MainPage() { InitializeComponent(); acceleroter = new Accelerometer(); acceleroter.TimeBetweenUpdates = TimeSpan.FromMilliseconds(1000); acceleroter.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(acceleroter_CurrentValueChanged); acceleroter.Start(); } /// <summary> /// acceleroter変化時に呼び出されるメソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void acceleroter_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) { try { AccelerometerReading accelerometer = e.SensorReading; Vector3 acceleration = accelerometer.Acceleration; Debug.WriteLine("X:" + acceleration.X.ToString() + " Y:" + acceleration.Y.ToString() + " Z:" + acceleration.Z.ToString()); } catch (Exception ex) { Dispatcher.BeginInvoke(() => this.textBlock1.Text = ex.Message); } } }
<まとめ>
特にこれと言って難しいところはありませんでしたが、しいて挙げるとすればAccelerometerのイベントは別スレッドで発生するようなので、Invokeを使ってUIスレッドに処理を渡すところは気をつけないといけません。
BackgroundWorkerについて(WP7)
<はじめに>
ストアアプリにはasync/awaitを始めとした非同期処理は必須という話をよく聞きます。それならばWindowsPhoneだって必須だと思うのですが、やり方を知らなかったので調べてみました。どうやらBackgroundWorkerを使って処理をバックグラウンドにまわせるようです。
<プログラム>
public partial class MainPage : PhoneApplicationPage { /// <summary> /// クラス変数 /// </summary> private BackgroundWorker bw = new BackgroundWorker(); private bool flag = true; #region コンストラクタ・初期化メソッド /// <summary> /// コンストラクタ /// </summary> public MainPage() { InitializeComponent(); Initialize(); } /// <summary> /// 初期化メソッド /// </summary> private void Initialize() { bw.WorkerReportsProgress = true; bw.WorkerSupportsCancellation = true; bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); } #endregion コンストラクタ・初期化メソッド #region イベント用メソッド /// <summary> /// プロセス実行用メソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = 1; (i <= 10); i++) { if ((worker.CancellationPending == true)) { e.Cancel = true; break; } else { // Perform a time consuming operation and report progress. System.Threading.Thread.Sleep(500); worker.ReportProgress((i * 10)); } } } /// <summary> /// Process完了メソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if ((e.Cancelled == true)) { this.textBlockProcess.Text = "Canceled!"; } else if (!(e.Error == null)) { this.textBlockProcess.Text = ("Error: " + e.Error.Message); } else { this.textBlockProcess.Text = "Done!"; } } /// <summary> /// Processキャンセルメソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.textBlockProcess.Text = (e.ProgressPercentage.ToString() + "%"); } /// <summary> /// 実行ボタンクリックイベント用メソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonJikko_Click(object sender, RoutedEventArgs e) { if (bw.IsBusy != true) { bw.RunWorkerAsync(); } } /// <summary> /// 停止ボタンクリックイベント用メソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonTeishi_Click(object sender, RoutedEventArgs e) { if (bw.WorkerSupportsCancellation == true) { bw.CancelAsync(); } } /// <summary> /// メッセージボタンクリックイベント用メソッド /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonMessage_Click(object sender, RoutedEventArgs e) { if (flag) { this.textBlockMessage.Text = "ほげ"; } else { this.textBlockMessage.Text = "ふが"; } flag = !flag; } #endregion イベント用メソッド }
<まとめ>
非同期処理を使って重い処理なんかを極力バックグラウンドにまわすことで、画面が固まることを避けたいわけですが、WindowsPhone7だとこんな感じでかけますという話でした。WindowsPhone8バージョンは実機を手に入れてからぼちぼちやろうかなと。
ジェネリックな型と非ジェネリックな型について
<はじめに>
ジェネリックについて調べていたらこんな記述がありました。
ジェネリック コレクションを使用する状況
通常は、ジェネリック コレクションを使用することをお勧めします。それは、タイプ セーフの利点をすぐに得られるからです。
タイプセーフの利点?いまいちピンとこなかったので、サンプルプログラムを書いてみました。
<プログラム>
class Program { static void Main(string[] args) { GenericTest<string> genericTest = new GenericTest<string>(); genericTest.Add("スカルミリョーネ"); genericTest.Add("カイナッツォ"); genericTest.Add("バルバリシア"); genericTest.Add("ルビカンテ"); NonGenericTest nonGenericTest = new NonGenericTest(); nonGenericTest.Add(1192); nonGenericTest.Add("いいくに作ろう鎌倉幕府"); nonGenericTest.Add(null); nonGenericTest.Add(3.14159265359); Console.WriteLine("// ジェネリック"); //-------------------------------------------------- // 指定した型のみを扱えばいいのでスッキリ! // foreach (var item in genericTest) { Console.WriteLine(item); } Console.WriteLine("// 非ジェネリック"); //-------------------------------------------------- // 何が来るかわからないのでイライラ >_<; // foreach (var item in nonGenericTest) { if (item == null) { continue; } Console.WriteLine(item.ToString()); } } } class GenericTest<T> : IEnumerable<T> { //-------------------------------------------------- // クラス変数(List) // private List<T> myList = new List<T>(); //-------------------------------------------------- // 要素を追加 // public void Add(T tempItem) { myList.Add(tempItem); } //-------------------------------------------------- // IEnumerable<T>.GetEnumeratorメソッド // public IEnumerator<T> GetEnumerator() { for (int i = 0; i < myList.Count; i++) { yield return myList[i]; } } //-------------------------------------------------- // IEnumerable.GetEnumeratorメソッド // IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class NonGenericTest : IEnumerable { //-------------------------------------------------- // クラス変数(List) // private ArrayList myList = new ArrayList(); //-------------------------------------------------- // 要素を追加 // public void Add(object tempItem) { myList.Add(tempItem); } //-------------------------------------------------- // IEnumerable.GetEnumeratorメソッド // IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < myList.Count; i++) { yield return myList[i]; } } }
<実行結果>
<まとめ>
ジェネリックコレクションだと要素の型のTが担保されるってことですね!一方非ジェネリックコレクションだと要素の型が担保されないから、クラス内で要素の追加時にhogehogeするか、foreachで回しながらfugafugaするしかないってことですね!
献立の中からカツを探し出すプログラム(LINQ)
<はじめに>
今週の献立の中で何曜日のメニューがカツなのか気になって仕方がない時ありますよね?
そこで、IEnumerable
<プログラム>
class Program { static void Main(string[] args) { MySampleClass<string> mySampleInstance = new MySampleClass<string>(); mySampleInstance.Add("月:カレー"); mySampleInstance.Add("火:トンカツ"); mySampleInstance.Add("水:ラーメン"); mySampleInstance.Add("木:スパゲティ"); mySampleInstance.Add("金:メンチカツ"); Console.WriteLine("// 今週の献立は…"); //-------------------------------------------------- // foreachにてデータを取得 // foreach (var item in mySampleInstance) { Console.WriteLine(item); } //-------------------------------------------------- // LINQにてデータを取得 // var list = mySampleInstance.Where(p => p.Contains("カツ")).Select(p => p.Split(':')); Console.WriteLine("// カツの曜日は…"); foreach (var item in list) { Console.WriteLine(item[0] + "曜日"); } } } class MySampleClass<T> : IEnumerable<T> { //-------------------------------------------------- // クラス変数(List) // private List<T> myList = new List<T>(); //-------------------------------------------------- // 要素を追加 // public void Add(T tempItem) { myList.Add(tempItem); } //-------------------------------------------------- // IEnumerable<T>.GetEnumeratorメソッドを実装(必須) // public IEnumerator<T> GetEnumerator() { for (int i = 0; i < myList.Count; i++) { yield return myList[i]; } } //-------------------------------------------------- // IEnumerable.GetEnumeratorメソッドを実装(必須) // IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
<実行結果>
<まとめ>
IEnumerable
yield returnを表面的に理解
<はじめに>
LINQについて調べていると、ところどころに出てくる
yield return hogehoge...
この「yield」というのがいまいちピンとこない!
そこでmsdnのドキュメントを眺めていると、いいのがありました!
反復子 (C# および Visual Basic)
Yield または yield return ステートメントに到達すると、コードの現在の位置が保持されます。 実装はその位置から反復子関数が呼び出されるときに再起動されます。
うーん…文章を眺めていてもわかりづらいので、実際に書いてみることに。
<プログラム1>
class Program { static void Main(string[] args) { foreach (int number in SomeNumbers()) { Console.Write(number.ToString() + " "); } } public static System.Collections.IEnumerable SomeNumbers() { yield return 3; yield return 5; yield return 8; } }
<実行結果1>
ほうほう!
ということは、こんな感じにしたら…
<プログラム2>
class Program { static void Main(string[] args) { foreach (int number in SomeNumbers()) { Console.Write(number.ToString() + " "); } } public static System.Collections.IEnumerable SomeNumbers() { for (int i = 0; i < 5; i++) { yield return i; } } }
<実行結果2>
なるほど、そういうことか!
<まとめ>
yield returnを表面的な理解の元日本語で書くとすると、
「呼び出し元からループで呼ばれると、
呼ばれた側は前回呼ばれたyieldの位置を覚えていて、
その次の文から処理を再開する」
ということですね。
拡張メソッドって何ぞ?
<はじめに>
msdnより
拡張メソッド (C# プログラミング ガイド)
拡張メソッドを使用すると、新規の派生型の作成、再コンパイル、または元の型の変更を行うことなく既存の型にメソッドを "追加" できます。
えーっ!?そんなことできるんですか!?
自分の不勉強をかみしめながら調べてみると、
拡張メソッドとしてstaticメソッドを用意しておくと、
あたかも他のクラスのインスタンスメソッドのように扱えるよ!ってことらしい。
<プログラム>
■拡張メソッドの実装
class Program { static void Main(string[] args) { string morning = "Morning"; morning.Hoge(); morning.Fuga("Good"); OtherClass otherClass = new OtherClass(); otherClass.Foo(); } } static class Kakucho { public static void Hoge(this string x) { Console.WriteLine(x); } public static void Fuga(this string x, string y) { Console.WriteLine(y + " " + x ); } public static void Foo(this OtherClass x) { Console.WriteLine(x.message); } } class OtherClass { public string message = "Hello World"; }
<実行結果>
<まとめ>
拡張メソッドを定義するときに、第一引数が対象のクラス、第二引数以降がメソッド呼び出し時の引数になるので注意。
慣れないと混乱するかもしれないけど難しいことはないです。
同じクラスに対して名前空間ごとに複数の拡張メソッドを定義することができるので、
例えば同じクラスを別の名前空間で使う時に、拡張メソッドを使って振る舞いを分けることができたりするのかな?
delegateを調べて試してみた
<はじめに>
delegateと聞くと、「イベントとメソッドを紐づけるときに使う何か」という程度の認識だったのですが、このままではいかん!と思って調べてみたらなかなか面白いではありませんか。
そこで、delegate型で少し遊んでみたのが下記
①delegate型のインスタンスを引数にして試してみた
②ジェネリックなdelegate型を試してみた
③匿名メソッドでdelegate型を試してみた
<プログラム>
class Program { static void Main(string[] args) { //----------------------------------------- // 変数の定義 // int[] x = { 1, 3, 5, 7, 11 }; string name = "nomurabbit"; //----------------------------------------- // delegate型のインスタンス // Hoge hogeGetDouble = new Hoge(GetDouble); Console.WriteLine("//// delegate型のインスタンス----"); TestMethod(hogeGetDouble, x); //----------------------------------------- // ジェネリックなdelegate // Fuga<string> fugaGetMessage = new Fuga<string>(GetMessage); Console.WriteLine("//// ジェネリックなdelegate------"); fugaGetMessage(name); //----------------------------------------- // 匿名メソッドでdelegate // Console.WriteLine("//// 匿名メソッドでdelegate------"); TestMethod(delegate(int y) { return y * 4; }, x); } #region delegateの定義 delegate int Hoge(int x); delegate void Fuga<T> (T x); #endregion #region delegateに渡すメソッド static int GetDouble(int x) { return x * 2; } static void GetMessage(string tempMessage) { Console.WriteLine("My name is " + tempMessage); } #endregion #region delegate型のインスタンスに第二引数を渡すメソッド static void TestMethod (Hoge h, int[] x) { foreach (var item in x) { Console.WriteLine(h(item).ToString()); } } #endregion }
<実行結果>
<まとめ>
なんと言ってもメソッドをインスタンスにして引数扱いできちゃうのが素敵だと思います。関数型っぽい(?)ですよね。
あとは、匿名メソッドが便利!メソッド名決めるのってなんだかんだ悩みますし、
delegate型に渡すちょっとしたメソッドが匿名で書けるのは気持ちいい!