- 投稿日:2020-02-07T17:23:24+09:00
CsvHelper ver 13 からの仕様変更に注意
c# で CSV を便利に扱えるライブラリ CsvHelper ですが、ver 13 から仕様変更があって、うっかりバージョンを上げるとはまります。
まずは、github の issues を一読。
https://github.com/JoshClose/CsvHelper/issues/1441
あと、change log も。
https://joshclose.github.io/CsvHelper/change-log/
従来のコードはこんな感じでしたが、
using (var csv = new CsvReader(reader)) { }ver 13 からは次のように書きます。
using System.Globalization; using (var csv = new CsvReader(reader, CultureInfo.CurrentCulture)) { }
- 投稿日:2020-02-07T17:08:46+09:00
C# の Dispose を正しく実装する
C# の Dispose を正しく実装する
IDisposable インターフェースの実装に焦点を絞った記事です。
using 構文による自動開放や、Finalizeや、GCのメカニズムについては、本記事末尾の資料をはじめとして、ネット上に良記事が沢山あるのでそちらを参考にしてください。IDisposable インターフェース
https://referencesource.microsoft.com/#mscorlib/system/idisposable.cs,1f55292c3174123d より抜粋
namespace System { [System.Runtime.InteropServices.ComVisible(true)] public interface IDisposable { void Dispose(); } }とてもシンプルです。
IDisposable が求めているのは、void Dispose()
を実装することのみです。
disposeパターンで述べられているvoid Dispose(bool disposing)
は実装を要求されていません。Dispose() のやるべき処理
void Dispose()
は次の条件を満たす必要があります
- 安全に複数回呼び出し可能であること
- インスタンスが保持しているリソースを解放すること
- 必要ならば、基本クラスのDisposeメソッドを呼び出すこと
- このクラスのファイナライズを抑制して、GCがファイナライズキュー上のオブジェクト数を減らすための手助けをする
- 予期せぬ非常に重大なエラー(たとえばOutOfMemoryException)を除いて、Disposeは例外をスローすべきではない。理想的には、Disposeを呼び出しにおいてオブジェクトに問題が発生しないようにする
ここで言うリソースとは、ファイルストリームやデータベースコネクションなどの、OSや外部DLLが管理するハンドルに紐づいたものです。
そして、C#のCLRの管理下にあるものをマネージドリソース、管理外にあるものをアンマネージドリソースと呼びます。
この両者の区別については細々とした議論がありますが、IDisposable の実装に際しての分類は次の通りです。IDisposable の実装におけるマネージドリソースとは
IDisposable インタフェースを実装したクラスのインスタンスです。
IDisposable の実装におけるアンマネージドリソースとは
IntPtrなどCLR外部(OSやDLLなど)のAPIから得たハンドル値です。
それらのハンドル値は、SafeHandle の派生クラスでラップすることで、マネージドリソースに変換できます。
処理中の例外発生に対して安全になるので、特別な理由がない限りマネージドリソースに変換したほうが良いです。アンマネージドリソースを持つ場合の実装
disposeパターンに従って、マネージドリソースとアンマネージドリソースを開放します。
基底クラスはIDisposableを継承し、Dispose() と Dispose(bool disposing) と ファイナライザを実装します。
派生クラス側は、Dispose(bool disposing) のみをオーバライドします。// IDisposableを実装するベースクラスのdisposeパターン public class MyBaseClass : IDisposable { // Pointer to an external unmanaged resource. private IntPtr _handle; // Other managed resource this class uses. private Stream _stream; // Track whether Dispose has been called. private bool _disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // TODO: Dispose managed resources here. _stream.Dispose(); } // TODO: Free unmanaged resources here. OS_CloseHandle(_handle); _handle = IntPtr.Zero; // Note disposing has been done. _disposed = true; } } ~MyBaseClass() { Dispose(false); } } // 上記クラスを先祖に持つサブクラスのdisposeパターン. // public void Dispose() を上書きせず、void Dispose(bool disposing) を上書きする. class MySubClass : MyBaseClass { // Track whether Dispose has been called. private bool _disposed = false; protected override void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // TODO: Dispose managed resources here. } // TODO: Free unmanaged resources here. _disposed = true; // Call the base class implementation. base.Dispose(disposing); } } // ファイナライザでやるべきことは ~MyBaseClass() で済んでいるので、ファイナライザ ~MySubClass() は不要である. //~MySubClass() //{ // Dispose(false); //} }マネージドリソースのみを持つ場合の実装(disposeパターン)
disposeパターンに従って、マネージドリソースを開放します。
基底クラスはIDisposableを継承し、Dispose() と Dispose(bool disposing) を実装します。
派生クラス側は、Dispose(bool disposing) のみをオーバライドします。// ベースクラスのdisposeパターン. public class MyBaseClass : IDisposable { // Other managed resource this class uses. private Stream _stream; // Track whether Dispose has been called. private bool _disposed = false; public void Dispose() { Dispose(true); // GC.SuppressFinalize(this); // ファイナライザを持たないthisに対して SuppressFinalize(this) は no effect なので、呼び出しは無意味である. } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // TODO: Dispose managed resources here. _stream.Dispose(); } // Note disposing has been done. _disposed = true; } } // アンマネージドリソースを持たないので、ファイナライザ ~MyBaseClass() は不要である. } // サブクラスのdisposeパターン. // public void Dispose() を上書きせず、void Dispose(bool disposing) をオーバライドする. class MySubClass : MyBaseClass { // Track whether Dispose has been called. private bool _disposed = false; protected override void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // TODO: Dispose managed resources here. } _disposed = true; // Call the base class implementation. base.Dispose(disposing); } } // アンマネージドリソースを持たないので、ファイナライザ ~MySubClass() は不要である. }マネージドリソースのみを持つ場合の実装(disposeパターンを使わない簡易版)
アンマネージドリソースを持たない場合は、もっと単純に Dispose を実装するだけでOKです。
ただし派生クラスもふくめて絶対にアンマネージドリソースを持たないという保証が必要です。
もしもアンマネージドリソースを持ちたくなったら、SafeHandle の派生クラスでラップしてマネージドリソースに変換することをルール化しましょう。// ベースクラスのdispose簡易実装. public class MyBaseClass : IDisposable { // Other managed resource this class uses. private Stream _stream; // Track whether Dispose has been called. private bool _disposed = false; public void Dispose() { if (!_disposed) { // TODO: Dispose managed resources here. _stream.Dispose(); // Note disposing has been done. _disposed = true; } } } // サブクラスのdispose簡易実装. class MySubClass : MyBaseClass { // Track whether Dispose has been called. private bool _disposed = false; public void Dispose() { if (!_disposed) { // TODO: Dispose managed resources here. _disposed = true; // Call the base class implementation. base.Dispose(); } } }
bool _disposed
フラグを省略するコーディングも可能です。
処理対象のマネージドリソースが少ない場合は、このコーディングの方が簡潔で良いでしょう。public void Dispose() { // TODO: Dispose managed resources here. _stream?.Dispose(); _stream = null; }アンマネージドリソースもマネージドリソースを持たない場合
不要です。何もしなくて良いです。
参考資料
- 投稿日:2020-02-07T15:38:31+09:00
Unity で SQLite を使う基本的な使い方まとめ
Unity で SQLite を使う基本的な使い方まとめ
Unity の更新がはやいのと Android が x64 対策でいろいろと設定方法が変わっているように思いました。
困ったのは、今でも.so
ファイル形式をインポートして設定する方法がよく検索にあがることです。Windows 環境だと
.so
ファイルをコンパイルして用意するのは、なかなか困難ですが、現在はAAR
形式を公式の SQLite.org からダウンロードして、そのままインポートすれば OK になってました。記事にまとめたので、ショートカットをメモ。
Android の 64 ビット対策
Unity はビギナーなので、間違いがあったらすいません。(おしえてください)
- 投稿日:2020-02-07T14:06:26+09:00
【Unity】カレンダーの作成【Android/ios】
はじめに
ある日、アプリの制作にカレンダーを使用したくなりました。
「Unity カレンダー」で先生に尋ねてもあまり自分の用途に合った記事が出てこない…
あまりというかほぼ出てこない…なんで?カレンダーになんか恨みあるの?というわけで適当にパパっと作ってみました。
意外に詰まったので同じ境遇の方がいれば参考にしていただければ幸いです。こんな感じのです。
ボタンを並べる
カレンダー自体は、GameObjectにプレハブのButtonを7*6の42個配置していてそこに数字を入れている感じなのでまずはButtonのプレハブを作成します。
作成方法はこちらなど参考にしてください。
大きさなどはGameObjectでそろえるので適当で大丈夫ですがデフォルトの画像だと集合体恐怖症さんが発狂してしまうので無地の画像を適当に作成し、使用したほうがいいと思います。
作るのめんどい人はどうぞ。そしてPanelを新規作成し好みの大きさに合わせて、その子として新規オブジェクトを配置します。
このGameObjectにグリットレイアウトグループ
コンポーネントを追加します。これをすることでこのオブジェクトに配置されたボタンがサイズを合わせて並ぶようになります。
そして、オブジェクトを配置するスクリプトを書いていきます。Calendar.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System; public class Calender : MonoBehaviour { public GameObject canvas;//エディタから指定 public GameObject prefab;//エディタから指定 void Start() { for (int i = 0; i < 42; i++) { GameObject button = Instantiate(prefab, canvas.transform); button.GetComponent<Button>(); } } }これをGameObjectに追加し、プレハブにはボタンのプレハブをCanvasにはGameObjectを追加してください。この状態で一度再生してみます。
ボタンが白のため死ぬほど見づらいですが42個配置されました。ちゃんと確認したい場合はプレハブのボタンの画像を変更してみてください。GameObjectの幅が狭いと横に7個配置されない場合があるので適宜調整してください。日付を入れる
このままでは白いボタンが42個配置されただけなのでここに日付を入力していきます。先ほど作ったCalender.csに以下を追加してみてください。
Calendar.cspublic static DateTime SelectDate; private DateTime D_Date; private int startday; private void CalendarController() { int days = 1; int overday = 1; D_Date = new DateTime(SelectDate.Year, SelectDate.Month, 1); //SelectDateの月の最初の日付 int year = SelectDate.Year; //年 int month = SelectDate.Month; //月 int day = SelectDate.Day; //日 //最初の日付の曜日を取得 DayOfWeek firstDate = D_Date.DayOfWeek; //何日まであるか int monthEnd = DateTime.DaysInMonth(year, month); //前月が何日まであるか SelectDate = SelectDate.AddMonths(-1); month = SelectDate.Month; SelectDate = SelectDate.AddMonths(1); int lastmonth = DateTime.DaysInMonth(year, month); switch (firstDate) { case DayOfWeek.Sunday: startday = 0; break; case DayOfWeek.Monday: startday = 1; break; case DayOfWeek.Tuesday: startday = 2; break; case DayOfWeek.Wednesday: startday = 3; break; case DayOfWeek.Thursday: startday = 4; break; case DayOfWeek.Friday: startday = 5; break; case DayOfWeek.Saturday: startday = 6; break; } int lastmonthdays = lastmonth - startday + 1; for (int i = 0; i < 42; i++) { if (i >= startday) { if (days <= monthEnd) { //文字を入れる Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DateTime tmp = D_Date;//一時変数 DayOfWeek num = tmp.DayOfWeek; //土曜日青・日曜日赤 switch (num) { case DayOfWeek.Sunday: DAY.GetChild(0).GetComponent<Text>().color = Color.red; break; case DayOfWeek.Saturday: DAY.GetChild(0).GetComponent<Text>().color = Color.blue; break; default: DAY.GetChild(0).GetComponent<Text>().color = Color.black; break; } DAY.GetChild(0).GetComponent<Text>().text = D_Date.Day.ToString(); D_Date = D_Date.AddDays(1); days++; } else { Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DAY.GetChild(0).GetComponent<Text>().color = Color.gray; DAY.GetChild(0).GetComponent<Text>().text = overday.ToString(); GameObject button = GameObject.Find("GameObject").transform.GetChild(i).gameObject; button.GetComponent<Button>().onClick.RemoveAllListeners(); overday++; } } else { Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DAY.GetChild(0).GetComponent<Text>().color = Color.gray; DAY.GetChild(0).GetComponent<Text>().text = lastmonthdays.ToString(); GameObject button = GameObject.Find("GameObject").transform.GetChild(i).gameObject; button.GetComponent<Button>().onClick.RemoveAllListeners(); lastmonthdays++; } } }死ぬほど見づらいコードだしなんか気持ち悪い箇所もあるかと思うのですが備忘録として書いているのものなのでお手柔らかにお願いします。(白目)
そしてvoid Start()
にSelectDate = DateTime.Now; CalendarController();はい、日付が登録されました。
SelectDate
をDateTime.Nowにしているので実行した月のカレンダーができていると思います。ここを変更すれば変更した月のカレンダーが表示されます。
カレンダーを表示したいだけの方はこれにて完了になります。ボタン押下時に値を保存する
自分はカレンダーで選択した値を使いたかったのでボタンを押した際の処理を追記していきます。
上のコードのFor分を以下に変更し、関数も追加してください。Calendar.csfor (int i = 0; i < 42; i++) { if (i >= startday) { if (days <= monthEnd) { //文字を入れる Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DateTime tmp = D_Date;//一時変数 DayOfWeek num = tmp.DayOfWeek; //土曜日青・日曜日赤 switch (num) { case DayOfWeek.Sunday: DAY.GetChild(0).GetComponent<Text>().color = Color.red; break; case DayOfWeek.Saturday: DAY.GetChild(0).GetComponent<Text>().color = Color.blue; break; default: DAY.GetChild(0).GetComponent<Text>().color = Color.black; break; } DAY.GetChild(0).GetComponent<Text>().text = D_Date.Day.ToString(); //以下3行追加 GameObject button = GameObject.Find("GameObject").transform.GetChild(i).gameObject; button.GetComponent<Button>().onClick.RemoveAllListeners(); button.GetComponent<Button>().onClick.AddListener(() => { set_Date(tmp); }); D_Date = D_Date.AddDays(1); days++; } else { Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DAY.GetChild(0).GetComponent<Text>().color = Color.gray; DAY.GetChild(0).GetComponent<Text>().text = overday.ToString(); GameObject button = GameObject.Find("GameObject").transform.GetChild(i).gameObject; button.GetComponent<Button>().onClick.RemoveAllListeners(); overday++; } } else { Transform DAY = GameObject.Find("GameObject").transform.GetChild(i); DAY.GetChild(0).GetComponent<Text>().color = Color.gray; DAY.GetChild(0).GetComponent<Text>().text = lastmonthdays.ToString(); GameObject button = GameObject.Find("GameObject").transform.GetChild(i).gameObject; button.GetComponent<Button>().onClick.RemoveAllListeners(); lastmonthdays++; } } void set_Date(DateTime date) { Debug.Log(date); //値を保存する処理など }これで実行してみます。
自分はわかりやすいようにテキストで表示していますが上のコードだとログで表示されるはずです。
以上になります。わかりづらいかもしれませんがお役に立てたら幸いです。
- 投稿日:2020-02-07T13:54:55+09:00
型制約の違いで関数オーバーロード
値型と参照型で同名の関数を定義したい
通常、C#では同じシグネチャで型制約が値型か参照型かだけが異なる関数を定義できない。
class Sample { public T GetValue<T>(string key) where T : struct { return default; } public T GetValue<T>(string key) where T : class { return default; } }に対しては次のようなエラーが出る。
型'Sample'は、'GetValue'と呼ばれるメンバーを同じパラメーターの型で既に定義しています
なぜかというとC#の型制約はシグネチャには含まれないから。
ハック
型制約をシグネチャで代用する。手を抜くなら次のような方法が楽。
class Sample { public T GetValue<T>(string key) where T : struct { return default; } public T GetValue<T>(string key, bool dummy = false) where T : class { return default; } }見た目が悪いがこれでも引数の違いによりオーバーロードができる。
あるいは
public class RequireClass<TClass> where TClass : class { } public class RequireStruct<TStruct> where TStruct : struct { }を定義したうえで
class SampleClass { public T GetValue<T>(string key, RequireStruct<T> helper = null) where T : struct { return default; } public T GetValue<T>(string key, RequireClass<T> helper = null) where T : class { return default; } }のようにシグネチャで型制約を表現すると引数の型から目的が読み取れて(本当か?)少しマシかもしれない。
もちろんどちらの方法でも値型の戻り値はT?にしてもよい。C#のシグネチャには戻り値の型は含まれないからだ。
いつ使うハックなのか
System.Collections.Hashtable(非ジェネリック版のDictionary)を使ったライブラリをラップするときに使った。
参考
Generic constraints, where T : struct and where T : class - stackoverflow
- 投稿日:2020-02-07T12:30:09+09:00
ASP.NETでAPIサーバーの返却をXMLしたい
ASP.NETでサーバのときXMLでデータ返却したいメモ
Microsoft.AspNetCore.Mvc.Formatters.Xmlインストール
NuGetで「Microsoft.AspNetCore.Mvc.Formatters.Xml」を参照してインストール(最新版がエラーだったので2.1.1使用)
Startup.csにoptons追加
RespectBrowserAcceptHeaderをtrueにしたらXMLになったのでメモ
public void ConfigureServices(IServiceCollection services) { services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddMvc(options => { // XMLを返すフォーマッターとそのMIME Typeと拡張子のマッピングを登録 // XmlDataContractSerializerOutputFormatterはASP.NET Web API相当 options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); // options.OutputFormatters.Add(new XmlSerializerOutputFormatter()); options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "application/xml"); options.RespectBrowserAcceptHeader = true; // false by default }); }ブラウザ表示
参考サイト
ASP.NET MVC Core 2.0で作ったAPIでXMLなどの形式を返したい
https://www.misuzilla.org/Blog/2017/08/26/ConfigureFormattersInAspNetMvcCore2C#(ASP.NET core)でWeb APIを作ってみる(Hello World編)
https://qiita.com/rawr/items/85abf5f646e20e3438a1#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%97%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8Bget%E3%81%A0%E3%81%91
- 投稿日:2020-02-07T04:02:35+09:00
Unityでシューティングゲームを作る(4)
ここまでの進捗
- 背景がループするようにした。
- 普通の敵の動作を作成し、その敵が3秒ごとに生成される。
- 瞬間移動する敵の動作を作成し、5秒ごとに生成する。
- プレイヤーが画面の範囲外に行かないようにした。
- 敵とプレイヤーが衝突したらプレイヤーが消滅する。
- 分散攻撃の敵を実装する。
- タイトルシーンとエンディングシーンを追加する。
今後やること
- ボスキャラの動作を実装する。
- エフェクトとBGMを追加する。
- プレイヤーが横に移動すると機体が傾くアニメーションをつける
- プレイヤーがダメージ受けると点滅して数秒だけ無敵状態になる
この記事では赤い部分を作成した。
プレイヤーがダメージを受けると点滅する
点滅するコルーチンを作成してプレイヤーの衝突判定の中でコルーチンを実行するようにした
コルーチンは関数内で時間を空けて処理をさせたいときに使われる点滅するコルーチンの作成に関してはこのサイトがとても参考になった。
ftvlog/オブジェクトを点滅させるスクリプト以下がコルーチンとなる
//ダメージを受けた時に点滅する IEnumerator Blink() { for(int i=0;i < 4; i++) { renderer.enabled = !renderer.enabled; yield return new WaitForSeconds(0.2f); } }この中でオブジェクトの点滅を0.2秒間隔で4回行っている
これによってダメージを受けると以下のようになった
プレイヤーがダメージを受けて点滅してる間は無敵になる
今回はプレイヤーが攻撃を受けてから点滅している間だけプレイヤーのレイヤーを変更することによって衝突判定を無効化した。
teratailに質問があったので参考にした。
teratail/衝突判定を一定時間無効化したいですまずレイヤーを追加するために[Edit]→[Project Settings]を選択する。
この設定画面がでるので左の欄から[Tags and Layers]を選択し、Layersの中からUser Layerのどれかに名前を付ける
今回はUser Layer 9にInvisibleという名前を付けた。最後に左の欄からPhysicsを選択し、衝突を無効化したいオブジェクトがあるレイヤーと先ほど作成したレイヤーのチェックを外す。
これによってInvisibleのレイヤにあるオブジェクトとDefaultにあるオブジェクトの衝突が無効化される。ちょっと見ずらいかもしれないけど、点滅時間が1.2秒で短いので一瞬しか無効にならない。
今後調整していこうと思う。これで弾を受けたときの点滅と無敵時間を作ることができた。
難易度をあげるにつれて点滅時間の延長も検討していきたい。次回はアニメーションをつけてボスを作成して完成させたい。
ちなみにここからこだわるつもり
- 投稿日:2020-02-07T00:55:53+09:00
ファイルの読み書き
はじめに
ただの備忘用のメモです。
ファイルの入出力ってたまに使うんですが何故か思い出せないことが多いです (´-ω-`)環境
Microsoft Visual Studio Community 2019 Version 16.4.3 .NET Core 3.1書く、読む
FileStream, StreamWriter, StreamReader を使います。
オーバーロードがいろいろあるんですがその中から目についたモノを書いておきます。
※詳細は[参考]を参照using System; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp7 { class Program { static async Task Main(string[] args) { // ファイルパスを渡すと内部でFileStreamが生成されてそこにデータが流し込まれる // StreamWriterにMemoryStreamとかを渡せば書き込み先を変えられる using (var writer = new StreamWriter(@".\test.txt", append: true, encoding: Encoding.UTF8)) { writer.WriteLine("hogeee"); // 非同期でも書ける await writer.WriteLineAsync("hunngaaa!!"); } using (var stream = new FileStream(@".\test.bin", FileMode.Append, FileAccess.Write)) { stream.Write(new byte[] { 0x66, 0x32, 0x87, 0x12, 0x11, 0x88, 0x45, 0x23 }); // バイナリの場合も非同期で書ける await stream.WriteAsync(new byte[] { 0x76, 0x12, 0x23, 0x99, 0x50, 0x43, 0x18, 0x66 }); } // StreamWriterと同じ要領で使える using (var reader = new StreamReader(@".\test.txt", Encoding.UTF8)) { Console.WriteLine(reader.ReadLine()); Console.WriteLine(await reader.ReadLineAsync()); } // あんまり使わない using (var stream = new FileStream(@".\test.bin", FileMode.Open, FileAccess.Read)) { var buffer = new byte[8]; stream.Read(buffer, 0, 8); Console.WriteLine(string.Join(", ", buffer.Select(b => $"{b,2:x2}"))); await stream.ReadAsync(buffer, 0, 8); Console.WriteLine(string.Join(", ", buffer.Select(b => $"{b,2:x2}"))); } } } }参考
FileStream クラス
StreamWriter クラス
StreamReader クラス
$ - 文字列補間
複合書式指定