- 投稿日:2020-02-15T21:06:18+09:00
GC allocとは
GC allocについてぼんやりしか知らなかったので調べてみた。
GC allocとは?
マネージヒープにオブジェクトを割り当てること
マネージヒープとは?
GCが管理するメモリ領域のこと。
スマホでアプリAを起動するとアプリAのプロセスが作成されて初期化されます。
初期化時GCは自身が管理する連続したメモリ領域を確保します。これをマネージヒープと呼びます。
C#の参照型はマネージヒープに割り当てられて管理されます。以下のようにHogeクラスをインスタンス化するとデータがマネージヒープ上に確保され、hoge変数がスタック領域に確保されます。
class Hoge { } void Main() { Hoge hoge = new Hoge(); }必要なメモリサイズ
64bit環境で参照型を管理するにはオブジェクトヘッダー(8byte)とメソッド情報へのポインタであるメソッドテーブルポインタ(8byte)が必要です。Hogeのような空のクラスをインスタンス化するとマネージヒープの16byteが確保されます。(32bit環境だと8byte)
クラスにメンバ変数が定義されていればそれらの型に必要なだけのsizeが追加で確保されます。例えばintのメンバ変数があれば4byte増えて全部で20byte確保されます。
参考
https://docs.unity3d.com/ja/2018.4/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html
https://docs.microsoft.com/ja-jp/dotnet/standard/automatic-memory-management
https://qiita.com/mima_ita/items/8303f2a476e8630f0728
https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-1-layout/
https://web.archive.org/web/20150515023057/https://msdn.microsoft.com/en-us/magazine/cc163791.aspx
https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-1-layout/
- 投稿日:2020-02-15T20:58:23+09:00
Azureみたいなウィザードっぽい入力フォームを作りたい Phase1 タブ実装編
Azureで仮想マシンの作成をすると、当然いろんな情報の入力を求められる。
入力項目が多いからか、ウィザードみたいな感じで情報入力がいくつかのフォームに分かれている。
上のスクショだと、「基本」「ディスク」「ネットワーク」...って感じ。
こんな感じのフォームを作ってみたい。
ということで、この前 .NET Coreに実装したAdminLTE3をベースに作成を開始。
1. タブの実装
AdminLTE3はBootstrap4を使っている。Bootstrapには「nav-tabs」というクラスが存在する。
こいつを使うと簡単にタブを実装できる。
<ul class="nav nav-tabs"> <li class="nav-item"> <a href="#tab1-content" class="nav-link" data-toggle="tab">基本</a> </li> <li class="nav-item"> <a href="#tab2-content" class="nav-link" data-toggle="tab">住所</a> </li> </ul> <div class="tab-content"> <div id="tab1-content" class="tab-pane active"> <p>基本の情報入力フォームの表示</p> </div> <div id="tab2-content" class="tab-pane"> <p>住所入力フォームの表示</p> </div> </div>これで、「基本の情報入力フォームの表示」の部分を入力フォームっぽいHTMLに書き換えてやれば。。。。
こんな感じでタブは実装できた。
・・・・しかし、タブの下にカードタイトルあるとなんかうざいな・・・・消すか・・・・
- 投稿日:2020-02-15T20:48:51+09:00
WPFでTextBoxにdoubleをBindingする
WPFでTextBoxにdoubleをBindingすると
- 文字の末尾に小数点を入力できない
1.002
の2を消すと1.00
ではなく1
になるなどの動作になります。
これはBindingしている値をTextBoxにも即反映させるという動作になっているからです。
1.002
の末尾の2を消した際の動作は下記のようになります。
Text
が1.00
に更新- Binding Sourceのdoubleが
(double)1
に更新Text
が1
に更新雑な解決
起動時に
App.xaml.csSystem.Windows.FrameworkCompatibilityPreferences .KeepTextBoxDisplaySynchronizedWithTextProperty = false;と設定すれば解決です。
Bindingしている値をTextBoxにも即反映させないようにします。.NET Framework 4.5以前はこれがデフォルトです。
解決
Text
プロパティを更新する前に別のプロパティでチェックを行うことで期待する動作を実現します。
Text
は設定せずにDoubleTextのみを使います。<myControl:DoubleTextBox DoubleText="{Binding Double1, UpdateSourceTrigger=PropertyChanged}" />using System; using System.Windows; using System.Windows.Controls; using System.Windows.Data; public class DoubleTextBox : TextBox { public string DoubleText { get => (string)GetValue(DoubleTextProperty); set => SetValue(DoubleTextProperty, value); } public static readonly DependencyProperty DoubleTextProperty = DependencyProperty.Register( nameof(DoubleText), typeof(string), typeof(DoubleTextBox), new FrameworkPropertyMetadata( string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnDoubleTextChanged), null, true, UpdateSourceTrigger.LostFocus)); private static void OnDoubleTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox textBox) { var currentText = textBox.Text; var newText = (string)e.NewValue; if (currentText == newText) return; if ( double.TryParse(currentText, out var currentDouble) && double.TryParse(newText, out var newDouble) && currentDouble == newDouble ) return; textBox.Text = newText; } } protected override void OnTextChanged(TextChangedEventArgs e) { base.OnTextChanged(e); this.DoubleText = this.Text; } }動作の解説
これによって、
1.002
の末尾の2を消した際の動作は下記のようになります。
Text
が1.00
に更新DoubleText
が1.00
に更新- Binding Sourceのdoubleが
(double)1
に更新DoubleText
が1
に更新1
と1.00
はともにdouble値として等しいのでText
は更新されないTextが更新された場合の動作
キーの入力などで
Text
が更新されたときはその値がそのままDoubleText
に反映されます。
Binding Sourceへも同様に反映されます。DoubleText(あるいはBinding Source)が更新された場合の動作
基本的には
Text
に反映されます。
ただし、DoubleText
とText
をそれぞれdoubleに変換して同じ値になる場合はText
を更新しません。
- 投稿日:2020-02-15T20:27:59+09:00
NeosVRでリアルタイム翻訳機を使う
はじめに
こんにちは!tuttuです!
twitterで紹介した、こちらのリアルタイム翻訳機の導入方法を説明します!
#NeosVR 海外の人がつくったリアルタイム翻訳機だよ!すごすぎ!
— tuttu@VRChat始めました (@tuttu74459297) February 12, 2020
今日はちょっと趣向を変えて紹介動画にしてみたよ! pic.twitter.com/hH2eMJ0Rptちなみにこれは、海外のAnomalousさんが作ったもので、本人の紹介動画もあります。
https://www.youtube.com/watch?v=-Ygun89yHZUわかりづらいところ、間違っているところがあれば随時加筆修正するので、
お気軽に教えてください!まとめ
最初に要点を書いておきます。
- 環境構築: C# .NET Framework 4.6.2, 4.8 で動作確認済み
- Azure認証キーの取得(無料試用版あり): Speech API (Cognitive Service)
- ソースコードのclone: https://github.com/Anomalous/AnomalousNeosExperiments/tree/master/Translator
- NeosTranslateDataModel.csの認証キーと言語を設定
- Translatorをローカルで立ち上げ
- (Neosにて)ツール配布ワールドでツールを取得: http://cloudx.azurewebsites.net/open/world/U-Anomalous/R-55501fd5-8e98-40db-9dd4-e2cae7ea41c6
- ワールドの動画に沿って、自分の名前を設定
1~5は現実世界で行い、6,7はNeosVRで行います。
これらについて次の章から詳しく見ていきましょう!動作確認環境
- Windows10
- Oculus Quest + Virtual Desktop
また、PCにマイクの音声が届いていることを確認しておきましょう。
現実世界編
C#環境構築
Visual Studioのインストール
Visual Studioをインストールするのが一番手っ取り早いかと思います。
https://qiita.com/grinpeaceman/items/b5a6082f94c9e4891613
こちらのページの「Visual Studioインストール」まで進めてください。(以下の話はVisual Studio 2019で進めています)
※補足
ふららんさん曰く、Visual Studio 2017で起動しなかったとのこと。
2019を使うのが無難かもしれません。
各Visual Studioの対応機能については知見がないので、わかる方がいたら教えていただけるとありがたいです。
tuttuさんのツイートに貼ってある記事の補足。VisualStudio2017ではエラーが出てサーバー立ち上がらないです。
— ふららん(furarann)@Nket広報担当 今日も一日生き延びてえらいっ!集会やってます! (@furarann_VR37) February 17, 2020
記事通りVisualStudio2019で起動すればサーバー立ち上がります。 https://t.co/LTWhi2FiUk.NET Frameworkのインストール
次に、.NET Frameworkをインストールします。
4.6.2~4.8のバージョンであればどれも動くと思いますが、僕は4.6.2と4.8で動くことを確認しました。
(参考: .NET Framework のバージョンの互換性)こちらから4.8のDeveloper Packのインストーラーをダウンロードし、
Azure Speech API 認証キーの取得
Microsoft/Facebook/Github/LinkedIn のいずれかのアカウントが必要です。
(Microsoftのアカウントは、WindowsのOSセットアップのときに作っていることが多いです)
持っていなければ、こちらから作りましょう。Azure Cognitive Serviceのページに行き、Speech APIタブをクリック
Speech Serviceの「APIキーの取得」をクリック
「7日間の試用期間」をクリックします。
(有効期限があるので、本格的に使う場合は課金する必要があります)
ここでログインを求められるので、持っているアカウントでログインします。
ログインすると、認証キーが2つ表示されます。
こちらの認証キーは後ほど使います。
翻訳機サーバーの立ち上げ
ソースコードのダウンロード
https://github.com/Anomalous/AnomalousNeosExperiments
こちらのソースコードをダウンロードします。
gitがあればcloneしてください。ない場合は、zipでダウンロードし、好きな場所に解凍します。
(基本的には日本語を含まないパスに解凍するのが良いです)
Visual Studioで開く
Visual Studioを立ち上げ、「プロジェクトやソリューションを開く」から先ほどのソースコードを開きましょう。
cloneもしくは解凍したAnomalousNeosExperimentsの下(もしくはAnomalousNeosExperiments-masterの2つ下)にTranslatorというフォルダがあります。
その中にある「NeosTextTranslator.sln」をVisual Studioから開きます。
認証キーや言語の設定
開いたNeosTextTranslatorのプロジェクトに対し、先ほどの認証キーや言語の設定をしていきます。
1. NeosTranslateDataModel.cs を開く
2. 先ほど取得したAzureの認証キー(2つあるうちのどちらでも良い)をDEFAULT_AZURE_SUBSCRIPTION_KEY
にセット
3. 入力側(話す方)の言語をTRANSLATOR_INPUT_LANGUAGE
にセット(これを読んでいる人はja-JP
で良いはず)
- 翻訳先の言語はNeos側で設定します
サーバーの立ち上げ
Visual Studio上のツールバーにある「開始」を押します。
試しに、http://localhost:8083/SetLanguage?Language=en などを叩いてみると、サーバーが反応していることを確認できます。
NeosVR編
NeosVRそのものの始め方は
https://neosvrjp.memo.wiki/
を参考にしてみてください!最近開設された、日本人向けNeosVRコミュニティのDiscordもあります。
https://discord.gg/GH8YRbd翻訳機配布ワールドで翻訳機をゲット!
http://cloudx.azurewebsites.net/open/world/U-Anomalous/R-55501fd5-8e98-40db-9dd4-e2cae7ea41c6
こちらのリンクを開くと、翻訳機を配布しているワールドに飛べます。
(NeosVRを立ち上げた状態で開くと確実です)ワールドでは、Anomalousさんの動画と翻訳機、そして翻訳機のインスペクターがお出迎えしてくれます。
この場で翻訳機をセットアップ(下記)してしまい、保存しましょう。
翻訳機のセットアップ
やることは2つです。
1. 翻訳機にユーザー名を登録する
2. 翻訳先の言語を登録するまず翻訳機にユーザー名を登録する必要があります。
ChatTranslatorのsettingsのインスペクターが表示されているので、この中にあるValueを自分の名前に書き換えましょう。
続いて、翻訳先の言語を登録します。
翻訳機の上にja
と入っていますが、試しにこちらをen
に変更してみましょう。
変更したら、Set Language
ボタンを押します。
(その他の言語はこちらにコード表があります)
すると、ここまでうまくできていれば翻訳機が動きます!
完了したら、翻訳機をインベントリーに保存しましょう。
(ユーザー作成ツールの保存方法はこちらを参考に)
動かないときのチェックポイント
- サーバーを立ち上げるときにエラーが出る
- .NET Frameworkのバージョンが違うかもしれません。僕は最初4.6.1でやろうとしてうまく立ち上がりませんでした
- ソースコードの変なところを編集してしまったかもしれません。認証キー周りのソースコードを見直してみましょう
- 最後まで完了したけど翻訳機が反応しない
- ユーザー名を翻訳機に正しく設定できていない可能性があります
- Set Languageを押したら青い文字は表示されたけど、翻訳結果が出てこない
- ソースコードに設定を入れるときに、Azureの認証キーや言語の設定を間違ってしまった可能性があります
- 音声がPCに届いていない可能性があります。PCの設定からマイクを確認してみましょう
- 投稿日:2020-02-15T12:54:03+09:00
System.Text.Json での派生クラスのシリアライズ
このドキュメントの内容
System.Text.Json.JsonSerializer クラスでの派生クラスのシリアライズについて簡単に説明します。
詳しくは、マイクロソフトの .NET ガイド の派生クラスのプロパティのシリアル化の項で説明されています。サンプルクラス
ローカルファイルまたはデータベースをデータソースとするアプリケーションの動作設定を、次のようなコンフィグクラスで表したとします。FileConfig クラスと DatabaseConfig クラスは DataSourceConfig クラスから派生しています。
public class ApplicationConfig { public DataSourceConfig DataSource { get; set; } } public class DataSourceConfig { public string Name { get; set; } } public class FileConfig : DataSourceConfig { public string FilePath { get; set; } } public class DatabaseConfig : DataSourceConfig { public string ConnectionString { get; set; } }シリアライズ対象のインスタンス自身が派生型である場合
単純にシリアライズしたときの結果
private string SerializeDataSourceConfig() { // 基底型で宣言 DataSourceConfig config = new FileConfig() { Name = "file1", FilePath = @"d:\test.csv" }; return JsonSerializer.Serialize(config, GetOptions()); } private string SerializeFileConfig() { // 派生型で宣言 FileConfig config = new FileConfig() { Name = "file1", FilePath = @"d:\test.csv" }; return JsonSerializer.Serialize(config, GetOptions()); } private JsonSerializerOptions GetOptions() { return new JsonSerializerOptions() { WriteIndented = true }; }シリアライザに渡された型の型情報から出力対象のプロパティが決定されます。基底型である DataSourceConfig クラスが渡された場合、派生型で定義されているプロパティは出力対象になりません。
SerializeDataSourceConfigメソッドの戻り値{ "Name": "file1" }
SerializeFileConfigメソッドの戻り値{ "FilePath": "d:\\test.csv", "Name": "file1" }
派生型で定義されているプロパティを出力対象にするには
Serialize メソッドの引数にシリアライズ対象インスタンスの型を渡すか、Serialize メソッドの型情報に object を指定します。
private string SerializeDataSourceInheritConfig1() { // 基底型で宣言 DataSourceConfig config = new FileConfig() { Name = "file1", FilePath = @"d:\test.csv" }; // 引数でインスタンスの型を渡す return JsonSerializer.Serialize(config, config.GetType(), GetOptions()); } private string SerializeDataSourceInheritConfig2() { // 基底型で宣言 DataSourceConfig config = new FileConfig() { Name = "file1", FilePath = @"d:\test.csv" }; // ジェネリックパラメーターで object を指定する return JsonSerializer.Serialize<object>(config, GetOptions()); }どちらも出力結果は同じです。FileCondig クラスで定義されているプロパティの値も出力されます。
SerializeDataSourceInheritConfig1メソッドの戻り値{ "FilePath": "d:\\test.csv", "Name": "file1" }
SerializeDataSourceInheritConfig2メソッドの戻り値{ "FilePath": "d:\\test.csv", "Name": "file1" }
シリアライズ対象インスタンスのプロパティに、その派生型のインスタンスが格納されている場合
単純にシリアライズしたときの結果
private string SerializeApplicationConfig() { ApplicationConfig config = new ApplicationConfig() { DataSource = new FileConfig() { Name = "file1", FilePath = @"d:\test.csv" } }; return JsonSerializer.Serialize(config, GetOptions()); }ApplicationConfig.DataSource の型は DataSourceConfig クラスであるため、FileConfig で定義されているプロパティは出力対象になりません。
SerializeApplicationConfigメソッドの戻り値{ "DataSource": { "Name": "file1" } }
派生型で定義されているプロパティを出力対象にするには
古典的な方法ですが、シリアライズ目的の object 型プロパティを定義します。本来のプロパティはシリアライズ対象外になるように属性でマークします。
前述の ApplicationConfig クラスを次のように変更します。
- DataSource プロパティに JsonIgnore 属性を付与します。
- DataSource プロパティの値も読み書きを行う object 型の DataSourceObject プロパティを定義します。
- JsonPropertyName 属性を付与し、"DataSource" という名前でシリアライズされるようにしています。
- Browsable 属性と EditorBrowsable 属性を付与し、表に現れにくくしています。
// using System.ComponentModel; // using System.Text.Json.Serialization; public class ApplicationConfig { [JsonIgnore] public DataSourceConfig DataSource { get; set; } [JsonPropertyName("DataSource")] [Browsable(false)] [EditorBrowsable( EditorBrowsableState.Never)] public object DataSourceObject { get { return DataSource; } set { DataSource = (DataSourceConfig)value; } } }FileConfig クラスで定義されているプロパティもシリアライズ対象になります。DataSourceObject プロパティの値がシリアライズされる際、JsonSerializer.Serialize メソッドに object 型が渡され、プロパティの型ではなくインスタンスの型が使用されるようになるためです。
SerializeApplicationConfigメソッドの戻り値{ "DataSource": { "FilePath": "d:\\test.csv", "Name": "file1" } }
まとめ
アプリケーションの動作設定をファイルから読み込むような目的であれば、この方法で問題はなさそうです。