20200215のC#に関する記事は5件です。

GC allocとは

GC allocについてぼんやりしか知らなかったので調べてみた。

GC allocとは?

マネージヒープにオブジェクトを割り当てること

マネージヒープとは?

GCが管理するメモリ領域のこと。

スマホでアプリAを起動するとアプリAのプロセスが作成されて初期化されます。
初期化時GCは自身が管理する連続したメモリ領域を確保します。これをマネージヒープと呼びます。
スクリーンショット 2020-02-15 0.54.40.png

C#の参照型はマネージヒープに割り当てられて管理されます。以下のようにHogeクラスをインスタンス化するとデータがマネージヒープ上に確保され、hoge変数がスタック領域に確保されます。

class Hoge
{
}

void Main()
{
    Hoge hoge = new Hoge();
}

スクリーンショット 2020-02-15 20.37.05.png

必要なメモリサイズ

64bit環境で参照型を管理するにはオブジェクトヘッダー(8byte)とメソッド情報へのポインタであるメソッドテーブルポインタ(8byte)が必要です。Hogeのような空のクラスをインスタンス化するとマネージヒープの16byteが確保されます。(32bit環境だと8byte)
クラスにメンバ変数が定義されていればそれらの型に必要なだけのsizeが追加で確保されます。例えばintのメンバ変数があれば4byte増えて全部で20byte確保されます。
スクリーンショット 2020-02-15 20.52.39.png

参考

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/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azureみたいなウィザードっぽい入力フォームを作りたい Phase1 タブ実装編

Azureで仮想マシンの作成をすると、当然いろんな情報の入力を求められる。

image.png

入力項目が多いからか、ウィザードみたいな感じで情報入力がいくつかのフォームに分かれている。

上のスクショだと、「基本」「ディスク」「ネットワーク」...って感じ。

こんな感じのフォームを作ってみたい。

ということで、この前 .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に書き換えてやれば。。。。

image.png

image.png

こんな感じでタブは実装できた。

・・・・しかし、タブの下にカードタイトルあるとなんかうざいな・・・・消すか・・・・

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WPFでTextBoxにdoubleをBindingする

WPFでTextBoxにdoubleをBindingすると

  • 文字の末尾に小数点を入力できない
  • 1.002の2を消すと1.00ではなく1になる

などの動作になります。

これはBindingしている値をTextBoxにも即反映させるという動作になっているからです。

1.002の末尾の2を消した際の動作は下記のようになります。

  1. Text1.00に更新
  2. Binding Sourceのdoubleが(double)1に更新
  3. Text1に更新

雑な解決

起動時に

App.xaml.cs
System.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を消した際の動作は下記のようになります。

  1. Text1.00に更新
  2. DoubleText1.00に更新
  3. Binding Sourceのdoubleが(double)1に更新
  4. DoubleText1に更新
  5. 11.00はともにdouble値として等しいのでTextは更新されない

Textが更新された場合の動作

キーの入力などでTextが更新されたときはその値がそのままDoubleTextに反映されます。
Binding Sourceへも同様に反映されます。

DoubleText(あるいはBinding Source)が更新された場合の動作

基本的にはTextに反映されます。
ただし、DoubleTextTextをそれぞれdoubleに変換して同じ値になる場合はTextを更新しません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NeosVRでリアルタイム翻訳機を使う

はじめに

こんにちは!tuttuです!
twitterで紹介した、こちらのリアルタイム翻訳機の導入方法を説明します!

ちなみにこれは、海外のAnomalousさんが作ったもので、本人の紹介動画もあります。
https://www.youtube.com/watch?v=-Ygun89yHZU

わかりづらいところ、間違っているところがあれば随時加筆修正するので、
お気軽に教えてください!

まとめ

最初に要点を書いておきます。

  1. 環境構築: C# .NET Framework 4.6.2, 4.8 で動作確認済み
  2. Azure認証キーの取得(無料試用版あり): Speech API (Cognitive Service)
  3. ソースコードのclone: https://github.com/Anomalous/AnomalousNeosExperiments/tree/master/Translator
  4. NeosTranslateDataModel.csの認証キーと言語を設定
  5. Translatorをローカルで立ち上げ
  6. (Neosにて)ツール配布ワールドでツールを取得: http://cloudx.azurewebsites.net/open/world/U-Anomalous/R-55501fd5-8e98-40db-9dd4-e2cae7ea41c6
  7. ワールドの動画に沿って、自分の名前を設定

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の対応機能については知見がないので、わかる方がいたら教えていただけるとありがたいです。

.NET Frameworkのインストール

次に、.NET Frameworkをインストールします。

4.6.2~4.8のバージョンであればどれも動くと思いますが、僕は4.6.2と4.8で動くことを確認しました。
(参考: .NET Framework のバージョンの互換性)

こちらから4.8のDeveloper Packのインストーラーをダウンロードし、
net0.PNG

インストーラーを起動してインストールを進めます。
net1.PNG

Azure Speech API 認証キーの取得

Microsoft/Facebook/Github/LinkedIn のいずれかのアカウントが必要です。
(Microsoftのアカウントは、WindowsのOSセットアップのときに作っていることが多いです)
持っていなければ、こちらから作りましょう。

Azure Cognitive Serviceのページに行き、Speech APIタブをクリック
azure1.PNG

Speech Serviceの「APIキーの取得」をクリック
azure2.PNG

「7日間の試用期間」をクリックします。
(有効期限があるので、本格的に使う場合は課金する必要があります)
ここでログインを求められるので、持っているアカウントでログインします。
ログインすると、認証キーが2つ表示されます。
こちらの認証キーは後ほど使います。
azure3.PNG

azure4.PNG

翻訳機サーバーの立ち上げ

ソースコードのダウンロード

https://github.com/Anomalous/AnomalousNeosExperiments
こちらのソースコードをダウンロードします。
gitがあればcloneしてください。

ない場合は、zipでダウンロードし、好きな場所に解凍します。
(基本的には日本語を含まないパスに解凍するのが良いです)
translator.PNG

Visual Studioで開く

Visual Studioを立ち上げ、「プロジェクトやソリューションを開く」から先ほどのソースコードを開きましょう。
vs1.PNG

cloneもしくは解凍したAnomalousNeosExperimentsの下(もしくはAnomalousNeosExperiments-masterの2つ下)にTranslatorというフォルダがあります。
その中にある「NeosTextTranslator.sln」をVisual Studioから開きます。
vs2.PNG

認証キーや言語の設定

開いたNeosTextTranslatorのプロジェクトに対し、先ほどの認証キーや言語の設定をしていきます。
1. NeosTranslateDataModel.cs を開く
2. 先ほど取得したAzureの認証キー(2つあるうちのどちらでも良い)をDEFAULT_AZURE_SUBSCRIPTION_KEYにセット
3. 入力側(話す方)の言語をTRANSLATOR_INPUT_LANGUAGEにセット(これを読んでいる人はja-JPで良いはず)
- 翻訳先の言語はNeos側で設定します
vs3.PNG

サーバーの立ち上げ

Visual Studio上のツールバーにある「開始」を押します。
vs4.PNG

すると黒い画面が立ち上がれば成功です。
vs5.PNG

試しに、http://localhost:8083/SetLanguage?Language=en などを叩いてみると、サーバーが反応していることを確認できます。
vs6.PNG

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さんの動画と翻訳機、そして翻訳機のインスペクターがお出迎えしてくれます。
2020-02-15 19.30.21.jpg

この場で翻訳機をセットアップ(下記)してしまい、保存しましょう。

翻訳機のセットアップ

やることは2つです。
1. 翻訳機にユーザー名を登録する
2. 翻訳先の言語を登録する

まず翻訳機にユーザー名を登録する必要があります。
ChatTranslatorのsettingsのインスペクターが表示されているので、この中にあるValueを自分の名前に書き換えましょう。
2020-02-15 19.32.48_LI.jpg

続いて、翻訳先の言語を登録します。
翻訳機の上にjaと入っていますが、試しにこちらをenに変更してみましょう。
変更したら、Set Languageボタンを押します。
(その他の言語はこちらにコード表があります)
2020-02-15 19.33.37.jpg

すると、ここまでうまくできていれば翻訳機が動きます!
完了したら、翻訳機をインベントリーに保存しましょう。
2020-02-15 19.34.46.jpg

(ユーザー作成ツールの保存方法はこちらを参考に)

動かないときのチェックポイント

  • サーバーを立ち上げるときにエラーが出る
    • .NET Frameworkのバージョンが違うかもしれません。僕は最初4.6.1でやろうとしてうまく立ち上がりませんでした
    • ソースコードの変なところを編集してしまったかもしれません。認証キー周りのソースコードを見直してみましょう
  • 最後まで完了したけど翻訳機が反応しない
    • ユーザー名を翻訳機に正しく設定できていない可能性があります
  • Set Languageを押したら青い文字は表示されたけど、翻訳結果が出てこない
    • ソースコードに設定を入れるときに、Azureの認証キーや言語の設定を間違ってしまった可能性があります
    • 音声がPCに届いていない可能性があります。PCの設定からマイクを確認してみましょう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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"
  }
}

まとめ

アプリケーションの動作設定をファイルから読み込むような目的であれば、この方法で問題はなさそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む