20200329のC#に関する記事は8件です。

C# 自作クラスListのコピー(Deep Copy)

コピーは1種類じゃない?

プログラミングにおける変数のコピーには
・ディープコピー
・シャローコピー
の2種類があります。
それぞれを一言でいうと

ディープコピー:コピー先の変数を変更しても、コピー元には影響がない

ディープコピーの例
int a = 1;
int b = a;
b = 2;
Console.WriteLine("a={0}",a);
Console.WriteLine("b={0}",b);
実行結果
a=1
b=2

上の例では、bを変えてもaには影響が出ていません

シャローコピー:コピー先の変数を変更すると、コピー元にも変更が適用される

シャローコピーの例
List<int> a = new List<int> { 0, 0, 0};
List<int> b = a;
b[1] = 1;
Console.Write("a=");
foreach(var member in a) Console.Write("{0} ",member);
Console.Write("\nb=");
foreach(var member in b) Console.Write("{0} ",member);
実行結果
a=0 1 0 
b=0 1 0

bを変えると、aも変わってしまっています。

C#におけるコピー

上の例を見て頂ければわかる通り、C#においては「=」で代入すると
int、doubleなどの値型:ディープコピー
配列、Listなどの参照型:シャローコピー
となるようです。

そもそものコピーの目的として
「元のデータを変えたくないからコピーしてるんだ!」
という理由が相当数を占めていると思うので、
基本的には
ディープコピーの需要が高い
と思っています。
しかしC#の標準ライブラリには汎用的にディープコピーするメソッドがないので、
実装してみました。
2通りの方法で実装しています。

実装例1:シリアライズを利用

こちらを参考にさせて頂きました。
参考というよりほぼコピペです。
ディープなコピペをしてしまい申し訳ない、、

ディープコピー用拡張メソッド
public static T DeepClone<T>(this T src)
    {
    using (var memoryStream = new System.IO.MemoryStream())
    {
        var binaryFormatter
          = new System.Runtime.Serialization
                .Formatters.Binary.BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, src); // シリアライズ
        memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
        return (T)binaryFormatter.Deserialize(memoryStream); // デシリアライズ
    }
}
検証用の自作クラス([Serializable]の記載を忘れるとエラーが出ます)
[Serializable]
class originalClass
{
    public int i;
    public string s;
    public List<int> iList;
}
実行例
List<originalClass> a = new List<originalClass>{
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} },
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} }};
List<originalClass> b = a.DeepClone();
b[0].i = 1;
b[0].iList[0] = 1;
Console.Write("a=");
foreach (var mem1 in a)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
Console.Write("\nb=");
foreach (var mem1 in b)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
```:実行結果
a=0 ○ {0 0 }
0 ○ {0 0 }

b=1 ○ {1 0 }
0 ○ {0 0 }

bを変更してもaは変わっていないので、正常にディープコピーできていそうです。

実装例2:コンストラクタを利用してnew

コンストラクタを適切に定義すれば、newによりディープコピーができるようです。
ただし、多重に参照している場合、値型にたどりつくまで下層を走査する必要があります。
詳しくは下記参考をご参照ください

検証用の自作クラス+コンストラクタ
class originalClass
{
    public int i;
    public string s;
    public List<int> iList;
    public originalClass(){}
    public originalClass(originalClass src)
    {
        i = src.i;
        s = src.s;
        iList = new List<int>();
        foreach(var mem in src.iList) iList.Add(mem);
    }
}
実行例
List<originalClass> a = new List<originalClass>{
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} },
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} }};
List<originalClass> b = new List<originalClass>();
foreach (var mem in a) b.Add(new originalClass(mem));
b[0].i = 1;
b[0].iList[0] = 1;
Console.Write("a=");
foreach (var mem1 in a)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
Console.Write("\nb=");
foreach (var mem1 in b)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
実行結果
a=0 ○ {0 0 }
0 ○ {0 0 }

b=1 ○ {1 0 }
0 ○ {0 0 }

bを変更してもaは変わっていないので、正常にディープコピーできていそうです。

参考:C#におけるシャローコピーとディープコピー

どんな場合がシャローコピー、どんな場合がディープコピーとなるか調べてみました

上の例を見ると、newすればコンストラクタが勝手にディープコピーしてくれるように見えます。
実行例を下記します。

値型のリストの場合

newによるListのディープコピー
List<int> a = new List<int> { 0, 0, 0};
List<int> b = new List<int>(a);
b[1] = 1;
Console.Write("a=");
foreach(var member in a) Console.Write("{0} ",member);
Console.Write("\nb=");
foreach(var member in b) Console.Write("{0} ",member);
実行結果
a=0 0 0 
b=0 1 0

bを変更してもaは変わっておらず、ディープコピーされているようです

多重リストの場合

次のような多重リストではどうでしょう?

newによる2重Listのコピー
List<List<int>> a = new List<List<int>>{
    new List<int> { 0, 0, 0 },
    new List<int> { 0, 0, 0 },
    new List<int> { 0, 0, 0 } };
List<List<int>> b = new List<List<int>>(a);
b[1][1] = 1;
Console.Write("a=");
foreach (var mem1 in a)
{
    foreach (var mem2 in mem1) Console.Write("{0} ", mem2);
    Console.Write("\n");
}
Console.Write("\nb=");
foreach (var mem1 in b)
{
    foreach (var mem2 in mem1) Console.Write("{0} ", mem2);
    Console.Write("\n");
}
実行結果
a=0 0 0
0 1 0
0 0 0

b=0 0 0
0 1 0
0 0 0

bと一緒にaまで変更されてしまっており、シャローコピーとなっているようです。
直下のリストはディープコピーされるが、そのリストが示す参照先はコピー元と共通、
といった感じと思われます。

自作クラスListの場合

データベース的な使い方をよくする、自作クラスListに対して、
よくありそうな3パターンに分けてコンストラクタによるnewの挙動を、
調べたいと思います。

newによるコピー結果検証用コード
List<originalClass> a = new List<originalClass>{
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} },
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} }};
List<originalClass> b = new List<originalClass>(a);
b[0].i = 1;
b[0].iList[0] = 1;
Console.Write("a=");
foreach (var mem1 in a)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
Console.Write("\nb=");
foreach (var mem1 in b)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}


パターン1:コンストラクタ定義なし

クラス定義
class originalClass
{
    public int i;
    public string s;
    public List<int> iList;
}
実行結果
a=1 ○ {1 0 }
0 ○ {0 0 }

b=1 ○ {1 0 }
0 ○ {0 0 }

中身が値型だろうが容赦なしでシャローコピーされています



パターン2:コンストラクタ + foreachでコピー

クラスの定義
class originalClass
{
    public int i;
    public string s;
    public List<int> iList;
    public originalClass(){}
    public originalClass(originalClass src)
    {
        i = src.i;
        s = src.s;
        iList = src.iList;
    }
}
検証用コードの変更(コピー部分のみ抜粋)
List<originalClass> a = new List<originalClass>{
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} },
    new originalClass{ i = 0, s = "○", iList = new List<int> { 0, 0} }};
List<originalClass> b = new List<originalClass>();
foreach (var mem in a) b.Add(new originalClass(mem));
b[0].i = 1;
b[0].iList[0] = 1;
Console.Write("a=");
foreach (var mem1 in a)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
Console.Write("\nb=");
foreach (var mem1 in b)
{
    Console.Write("{0} ", mem1.i);
    Console.Write("{0} ", mem1.s);
    Console.Write("{");
    foreach (var mem2 in mem1.iList) Console.Write("{0} ", mem2);
    Console.Write("}");
    Console.Write("\n");
}
実行結果
a=0 ○ {1 0 }
0 ○ {0 0 }

b=1 ○ {1 0 }
0 ○ {0 0 }

値型はディープコピー、参照型(List)はシャローコピーされています

パターン3:パターン2 + コンストラクタのリスト部分のみforeachで1要素ずつ代入

クラスの定義
class originalClass
{
    public int i;
    public string s;
    public List<int> iList;
    public originalClass(){}
    public originalClass(originalClass src)
    {
        i = src.i;
        s = src.s;
        iList = new List<int>();
        foreach(var mem in src.iList) iList.Add(mem);
    }
}
実行結果
a=0 ○ {0 0 }
0 ○ {0 0 }

b=1 ○ {1 0 }
0 ○ {0 0 }

ついにディープコピーが実現できました(bを変えてもaは変わらない)
このパターン3は、上の実装例2と同じものです。



結論としては、参照型が内部に存在する場合、値型にたどり着くまで内部を辿ってから代入しないといけないみたいです。
複雑な自作クラスになるとどこが値型なのか探索するのも、コンストラクタの実装の手間も大きそうなので、
実装1の方法がシンプルでよさそうです。

まとめ:ディープコピーになる場合とシャローコピーになる場合

※自作リストの場合は上記参照ください

C#においてディープコピーとなる例
//値型を代入
int b = a;
//stringの代入(stringは参照型だが、例外的にディープコピーとなる)
string b = a;
//値型のListをコンストラクタでnew
List<int> b = new List<int>(a);
C#においてシャローコピーとなる例
//配列、リスト等の参照型を代入
int[] b = a;
List<int> b = a;
//多重Listをコンストラクタでnew
List<List<int>> b = new List<List<int>>(a);
//foreachの中身(下の例でmemberを変更すると、aも変更される)
foreach(var member in a)
//gourpbyの中身(下の例でgroupaを変更すると、aも変更される)
var groupa = a.Groupby(c => c.key)

foreachやgroupbyは、逆にディープコピーだとループ内での変更ができず困る場面もありそうなので、
シャローコピーで助かる面も多いかと思います。

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

Sign in with Apple を ASP.NET Core のウェブサイトに導入してみる

前書き

ASP.NET Core で作成したウェブサイトに Sign in WIth Apple を組み込む必要があったので、調べたついでに導入手順を書き残します。

需要があるかわかりませんが、GitHub にサンプルもあげておきます。
https://github.com/Yuki0928/SignInWIthAppleSample

ASP .NET Core では Facebook、Twitter、Google、Microsoft などの外部プロバイダーの認証を非常に簡単にアプリに組み込めるのですが、Sign in with Apple は勝手が違うところがあり少し手間でした。

作業環境

Windows 10
Visual Studio 2019

Apple 開発者ポータルでの事前準備

アップルのヘルプなどを参考に Sign in with Apple を使用する準備を行います。
https://help.apple.com/developer-account/#/devde676e696

主な作業は3つです。
- App ID の作成
- Service ID の作成
- Private Key の作成

個人的にはヘルプは少々わかりづらかったので、こちらの記事を参考に準備を行いました。
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple

ドメインと認証後のコールバック URL の登録 について

今回はローカルで動作確認を行うため、example.com と https://example.com:5001/signin-apple を登録し、hosts に 127.0.0.1 example.com のレコードを登録しました。
image.png
localhost で登録できればいいんですが、ちょっと面倒ですね。

ASP.NET Core Web アプリケーションを作成と動作確認

Visual Studio を起動して ASP.NET Core Web アプリケーション を作成します。
image.png

任意のプロジェクト名などを入力します。
image.png

Web アプリケーション(モデル ビュー コントローラー)を選択します。
image.png

Properties から LaunchSettings.json を開きます。
下のほうの applicationUrl の localhost を example.com に書き換えます。
image.png

IIS Express の横の小さな三角ボタンを押して、SignInWithAppleSample を選択します。
image.png

SignInWithAppleSample ボタンを押します。
image.png

証明書を信頼します。
image.png

Windows ファイアウォールの警告が表示された場合はアクセスを許可します。
image.png

ブラウザで警告が表示された場合は接続を許可します。
image.png
image.png

https://example.com:5001 で ASP.NET Core Web アプリケーションが動いていることが確認できました。このブラウザは一旦閉じます。
image.png

Microsoft.AspNetCore.Authentication.OpenIdConnect をインストール

NuGet で Microsoft.AspNetCore.Authentication.OpenIdConnect をインストールします。
image.png
image.png

TokenGenerator の作成

Model に TokenGenerator クラスを作成します。
image.png
image.png

TokenGenerator.cs のコードはこちらをコピペして上書きします。
iss、sub、kid、privateKey は自分の情報に書き換えます。(詳しくは後述)

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;

namespace SignInWithAppleSample.Models
{
    public static class TokenGenerator
    {
        public static string CreateNewToken()
        {
            const string iss = "TeamID をいれます";
            const string sub = "ServiceID をいれます";
            const string kid = "KeyID をいれます";
            const string privateKey = "PrivateKey をいれます";

            var cngKey = CngKey.Import(
              Convert.FromBase64String(privateKey),
              CngKeyBlobFormat.Pkcs8PrivateBlob);

            var handler = new JwtSecurityTokenHandler();
            var token = handler.CreateJwtSecurityToken(
                issuer: iss,
                audience: "https://appleid.apple.com",
                subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", sub) }),
                expires: DateTime.UtcNow.AddMinutes(5),
                issuedAt: DateTime.UtcNow,
                notBefore: DateTime.UtcNow,
                signingCredentials: new SigningCredentials(new ECDsaSecurityKey(new ECDsaCng(cngKey)) { KeyId = kid }, SecurityAlgorithms.EcdsaSha256));

            return handler.WriteToken(token);
        }
    }
}

TeamID と ServiceID は Apple の開発者ポータルから取得します。
image.png

KeyID とPrivateKey は開発者ポータルで Key を発行したときに AuthKey_xxxxxxxxxx.p8 というファイルをダウンロードしているはずですのでそちらから取得します。

KeyID は上記のファイル名の xxxxxxxxxx の部分です。

PrybateKey はファイル内の文字列の改行を削除して1行にしたものを入れます。
先頭の -----BEGIN PRIVATE KEY----- と末尾の -----END PRIVATE KEY----- も削除してください。

Sign in with Apple を組み込む

Startup.cs を開きます。
image.png

using を追加します。

using System.Net.Http;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using SignInWithAppleSample.Models;

ConfigureServices メソッド を以下のように書き換えます。
ServiceID は先ほどと同じものをいれます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(async options =>
    {
        options.ClientId = "ServiceID をいれます";
        options.CallbackPath = "/signin-apple";
        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.ResponseType = OpenIdConnectResponseType.Code;
        options.Scope.Clear();

        options.Configuration = new OpenIdConnectConfiguration
        {
            AuthorizationEndpoint = "https://appleid.apple.com/auth/authorize",
            TokenEndpoint = "https://appleid.apple.com/auth/token",
        };

        options.Events.OnAuthorizationCodeReceived = context =>
        {
            context.TokenEndpointRequest.ClientSecret = TokenGenerator.CreateNewToken();
            return Task.CompletedTask;
        };

        options.TokenValidationParameters.ValidIssuer = "https://appleid.apple.com";

        var jwks = await new HttpClient().GetStringAsync("https://appleid.apple.com/auth/keys");
        options.TokenValidationParameters.IssuerSigningKeys = new JsonWebKeySet(jwks).Keys;

        options.ProtocolValidator.RequireNonce = false;
    });
}

Configure メソッドに app.UseAuthentication(); を追加します。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseAuthentication(); // この1行を追加

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

HomeController.cs を開きます。
image.png

適当な場所に SignInWithApple メソッドを追加します。

public IActionResult SignInWithApple ()
{
    return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
}

Index.cshtml を開きます。
image.png

末尾に以下のコードを書き足します。

@if (User.Identity.IsAuthenticated)
{
    @User.Claims.First(x => x.Type.Contains("nameidentifier")).Value
}
else
{
    @Html.ActionLink("Sign in With Apple でサインインする", "SignInWithApple")
}

以上で導入は一旦完了です。
ログアウト処理を書いていませんが、Sign in with Apple だからといって変わったことはないのでここでは割愛します。

Sign in With Apple の動作確認

最初の動作確認と同様に Web アプリケーションをデバッグ実行します。
image.png

Sign in With Apple でサインインする のリンクをクリックします。
image.png

Apple の認証画面に飛ばされるのでサインインします。
image.png

サインインすると元の画面に戻ります。
Apple から取得したユーザーの ID が表示されます。
image.png

これでアプリやゲームに Sign in With Apple を組み込めますね!

参考

https://www.scottbrady91.com/OpenID-Connect/Implementing-Sign-In-with-Apple-in-ASPNET-Core#aspnetcore

https://docs.microsoft.com/ja-jp/aspnet/core/security/authentication/social/?view=aspnetcore-3.1&tabs=visual-studio

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

C# コンストラクタ初期化子

コンストラクタ初期化子について

経緯

先日仕事中、visual studioの自動補完を使っている際、
コンストラクタで見慣れない書き方を発見したので備忘録として記す。

実際のソース

以下に実際のソースを記す。

Bairitu.cs
    public class Bairitu
    {

    string mbairituName = string.Empty;
    string temp = string.Empty;

        public Bairitu(string bairituName)
        {
            this.mbairituName = bairituName;
        }

        public Bairitu( string bairituName,string temp ) : this(bairituName)
        {
        this.temp = temp;
        }
    }

解説

上記のBairituクラスでは

  • string型を引数にとるコンストラクタ
  • string型引数を二つとるコンストラクタ

上記二つのコンストラクタが記述されていることが確認できる。

ここで下記の方法でインスタンスを生成する

呼び出し
var test = new Bairitu("hoge", "fuge");

その結果メンバ変数の結果は下記のようになる

  • mbairituName == "hoge"
  • string temp == "fuge"

動きは下記の流れとなる

  1. 引数二つのコンストラクタが呼び出される。
  2. 引数二つのコンストラクタに「: this(bairituName)」が記述されているので引数一つのコンストラクタが実行される。
  3. 「this.mbairituName = bairituName;」コンストラクタの中身が実行される。
  4. 3.を実行後「this.temp = temp;」引数二つのコンストラクタが実行される。

注意点

本記事の内容には必要な前提の説明や補足がない場合がありますのでご注意ください
間違いがあれば編集リクエストまでお願いします。

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

お手軽Linux Guiアプリ開発 入門

お手軽Linux Guiアプリ開発 入門

VisualStudioMacだけでWindows Formsを作る感覚でLinuxGUIアプリが作れます

利点
ターミナルで開発環境を整えなくてよい。VisualStudioMacが自動で整えてくれます

VisualStudioMacをダウンロード

GTK#2を選択する
スクリーンショット 2020-03-29 12.52.41.png

ツールボックスを選択
スクリーンショット 2020-03-29 12.53.22.png

ツールボックスが表示される インターフェース Main Windowを選択
スクリーンショット 2020-03-29 12.53.49.png

FiexedをMain Windowにドラッグします。編集可能領域を作成します
スクリーンショット 2020-03-29 12.54.05.png

ボタンをドラッグします。ボタンを移動させるには白い四角をクリックしドラッグします
スクリーンショット 2020-03-29 12.54.25.png

プロパティを表示します。
スクリーンショット 2020-03-29 12.54.52.png

Clickedの文字の部分をダブルクリックします。クリックイベントメソッドが自動生成します。
スクリーンショット 2020-03-29 12.57.42.png

クリックイベントメソッドが自動生成されます。ラベルに文字を入れる処理を加えます
スクリーンショット 2020-03-29 12.58.07.png

実行します
スクリーンショット 2020-03-29 13.29.40.png

ボタンを押すと実行されます
スクリーンショット 2020-03-29 12.59.46.png

ビルドされたプログラムはBinファイルの中に生成されています
スクリーンショット 2020-03-29 13.42.47.png

ラズベリーパイで動作させるにはMonoDevelopをインストールします
スクリーンショット 2020-03-29 13.31.37.png

MonoDevelop

感想

VisualStudioのWindowsFormを作成する感覚でお手軽に作成できました。
WindowsのVusualStudioだとGUIデザイナーが組み込まれていないため、GladeやSteticといったGUIデザイナーをダウンロードし、
交互に使わないといけない。クリックなどのイベントも自分で書かないといけない。

Gladeをターミナルでダウンロードしたり、Monoの開発環境を手動で整えなくても、VisualStudioMacが自動で環境を作ってくれます。。
あとはWindows Formsを作る感覚でC#を書くだけで作れます。

mono stetic

参考

C# VisualStudio for Mac でGtk#を使ってみた。gtk-sharp

次回 開発予定

C#からPythonを実行する 参考 C#からPythonスクリプトを呼び出す
Dapperの導入 参考 Dapperについてまとめてみた
Dapper Extensionの導入 参考 Dapperの拡張ライブラリ 比較検討
DataGrideViewとModelのバインディングの実現 参考URL treeView
Swaggerを使う Stoplight Studioなどを使う  参考 本当に使ってよかったOpenAPI (Swagger) ツール
RazurebyPiへのデプロイの仕方
Sqlite3の利用

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

userSettingsとapplicationSettings

(アプリケーション名).exe.Configの中身について

  • Settings.settingsから編集する。
    image.png

  • スコープは、ユーザーとアプリケーションがある。ユーザは書き換えが可能、アプリケーションは固有。
    image.png

  • ユーザの場合、userSettings内に記載され、アプリケーションの場合は、applicationSettingsに記載される。

xxxx.exe.Configの中身
    <userSettings>
        <WindowsFormsApp1.Properties.Settings>
            <setting name="Setting1" serializeAs="String">
                <value>aaa</value>
            </setting>
        </WindowsFormsApp1.Properties.Settings>
    </userSettings>
    <applicationSettings>
        <WindowsFormsApp1.Properties.Settings>
            <setting name="Setting2" serializeAs="String">
                <value>bbb</value>
            </setting>
        </WindowsFormsApp1.Properties.Settings>
    </applicationSettings>

userSettings

読み出し
textBox1.Text = Properties.Settings.Default.Setting1

Save()で設定した値を保存する。

値の設定と保存
// 値の設定
Properties.Settings.Default.Setting1 = "a"
// 全部まとめて保存
Properties.Settings.Default.Save();
デフォルト値に戻す
Properties.Settings.Default.Reset();
  • 保存先は、C:\Users(ユーザ名)\AppData\Local(アプリケーション名)\xxxxxx\user.config にある。
  • デフォルト値は、(アプリケーション名).exe.Config

applicationSettings

  • 読み出しの方法はuseSettingと同じだが、保存ができない。

(メモ) 1つの設定値に複数の値を設定する

カンマで区切って文字列として設定しておき、Splitで分離する。

xxxx.exe.Config
<setting name="Setting1" serializeAs="String">
  <value>aaa,bbb,ccc</value>
</setting>
comboBox1.Items.AddRange(Properties.Settings.Default.Setting1.Split(','));

参考

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

C#からWordテンプレートに文字を挿入する方法

Word側の設定

  • 挿入 > クイックパーツ > フィールド
  • MergeFieldを選択
  • フィールド名に適当な名前をつける(c#から参照する名前。例:CustomerName)
using Microsoft.Office.Interop.Word;

// テンプレートのファイルパス
private string templete = @"C:\tmp\templete.docx";

public void InsertWord(string field, string word)
{
    object missing = Type.Missing;
    var app = new Application();
    var doc = app.Documents.Open(templete, ref missing, true);

    foreach (Field myMergeField in doc.Fields)
    {
        Range rngFieldCode = myMergeField.Code;
        String fieldText = rngFieldCode.Text;
        if (fieldText.StartsWith(" MERGEFIELD"))
        {
            Int32 endMerge = fieldText.IndexOf("\\");
            Int32 fieldNameLength = fieldText.Length - endMerge;
            string fieldName = fieldText.Substring(11, endMerge - 11);
            fieldName = fieldName.Trim();
            if (fieldName == field)
            {
                myMergeField.Select();
                app.Selection.TypeText(word);
            }
        }
    }

    // 保存パス
    doc.SaveAs(@"C:\tmp\output.docx");

    var doc_close = (_Document)doc;
    doc_close.Close();

    var applicationclose = (_Application)app;
    applicationclose.Quit();
}

参考

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

LINQでStdev (標準偏差)

背景

LINQの標準ライブラリには標準偏差を求めるメソッドがない。
なので拡張メソッドを自作
詳しくはこちら
※ちなみに、ここで求めているのは母集団標準偏差(ExcelのStdev.P)です

コード

アルゴリズムはこちらを参照させていただきました。
自乗和を先に求めるのがポイント

※注意
ジェネリック四則演算用クラス読込が必要
こちら参照ください

public static double Stdev<T>(this IEnumerable<T> src)
        {
            if(!src.Any()) throw new InvalidOperationException("Cannot compute median for an empty set.");
            //ジェネリックの四則演算用クラス
            var ao = new ArithmeticOperation<T>();
            //四則演算できない型のとき、例外を投げる
            if (!ao.ArithmeticOperatable(typeof(T))) throw new InvalidOperationException("Cannot compute arithmetic operation");
            //Doubleにキャストして処理を進める
            var doubleList = new List<double>();
            foreach (var srcRow in src) doubleList.Add(Convert.ToDouble(srcRow));

            //平均値算出
            double mean = doubleList.Average();
            //自乗和算出
            double sum2 = doubleList.Select(a => a * a).Sum();
            //分散 = 自乗和 / 要素数 - 平均値^2
            double variance = sum2 / doubleList.Count - mean * mean;
            //標準偏差 = 分散の平方根
            return Math.Sqrt(variance);
        }

結果

List<int> iList = new List<int> { 1, 2, 3, 4, 5 };
List<double> dList = new List<double> { 3.2, 3.5, 3.6, 4 };
//標準偏差(int)
Console.WriteLine(iList.Stdev().ToString());
//標準偏差(double)
Console.WriteLine(dList.Stdev().ToString());
1.4142135623731
0.28613807855649

正常に標準偏差が出せていそうです

苦労したところ

・Generic → doubleへのキャスト
平均や二乗和を求めるためのAverage()やSum()がGeneric型では使えない。
doubleにキャストしようとしたが、ここに書いてあるobjectキャストを挟む方法ではエラー発生
ここに書いてるConvert.ToDouble()でキャストできた

(foreachの中なので、キャスト部分の動作速度が若干不安ではありますが)

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

LinQでStdev (標準偏差)

背景

LinQの標準ライブラリには標準偏差を求めるメソッドがない。
なので拡張メソッドを自作
詳しくはこちら
※ちなみに、ここで求めているのは母集団標準偏差(ExcelのStdev.P)です

コード

アルゴリズムはこちらを参照させていただきました。
自乗和を先に求めるのがポイント

※注意
ジェネリック四則演算用クラス読込が必要
こちら参照ください

public static double Stdev<Type>(this IEnumerable<Type> src)
        {
            if(!src.Any()) throw new InvalidOperationException("Cannot compute median for an empty set.");
            //ジェネリックの四則演算用クラス
            var ao = new ArithmeticOperation<Type>();
            //四則演算できない型のとき、例外を投げる
            if (!ao.ArithmeticOperatable(typeof(Type))) throw new InvalidOperationException("Cannot compute arithmetic operation");
            //Doubleにキャストして処理を進める
            var doubleList = new List<double>();
            foreach (var srcRow in src) doubleList.Add(Convert.ToDouble(srcRow));

            //平均値算出
            double mean = doubleList.Average();
            //自乗和算出
            double sum2 = doubleList.Select(a => a * a).Sum();
            //分散 = 自乗和 / 要素数 - 平均値^2
            double variance = sum2 / doubleList.Count - mean * mean;
            //標準偏差 = 分散の平方根
            return Math.Sqrt(variance);
        }

結果

List<int> iList = new List<int> { 1, 2, 3, 4, 5 };
List<double> dList = new List<double> { 3.2, 3.5, 3.6, 4 };
//標準偏差(int)
Console.WriteLine(iList.Stdev().ToString());
//標準偏差(double)
Console.WriteLine(dList.Stdev().ToString());
1.4142135623731
0.28613807855649

正常に標準偏差が出せていそうです

苦労したところ

・Generic → doubleへのキャスト
平均や二乗和を求めるためのAverage()やSum()がGeneric型では使えない。
doubleにキャストしようとしたが、ここに書いてあるobjectキャストを挟む方法ではエラー発生
ここに書いてるConvert.ToDouble()でキャストできた

(foreachの中なので、キャスト部分の動作速度が若干不安ではありますが)

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