20200520のC#に関する記事は6件です。

C# 備忘録1

はじめに

業務でC#を使っていた際に詰まったところや忘れがちな所を備忘録として書きました。

  • スレッドやタスクからフォームを変更する場合

スレッドやタスクからフォームを操作するとエラーが発生する。
そのため、Invokeを使用する。

例:テキストボックス(textBox)の名称を変更する場合

sample.cs
   //フォーム上のテキストをChangedに変更
   this.Invoke(new Action<string>(this.TextChange),"Changed");

   //フォーム上のテキストボックスを変更する関数
   private void TextChange(string text)
   {
       this.textBox.Text = text;
   }

Invokeは引数の有無で以下のように使い分ける。

   //引数なし
   this.Invoke(new Action(関数));
   //引数あり
   this.Invoke(new Action<引数の型名>(関数),引数);

引数がない場合ならラムダ式を使用して、以下のように書くと便利

   //引数なし
   this.Invoke(new Action(() => {処理}));
  • 待機をする方法

例:100ミリ秒を待機する。

   System.Threading.Thread.Sleep(100)

usingを使用する場合はusing System.Threading;を宣言し、
以下のようにする。

   //100ミリ秒待機
   Thread.Sleep(100)
  • Consoleの入力

基本的にフォームを使うので、すぐに忘れてしまう。

   //文字
   string hoge1 = Console.Read();
   //1行分
   string hoge2 = Console.ReadLine();
  • Consoleの出力

入力と同じですぐに忘れてしまう

   string hoge = "test";
   //改行なし出力
   Console.Write(hoge);
   //改行あり出力
   Console.WriteLine(hoge);
  • 「」や()などで括る場合など

リソースに定義していても使えるので、便利

   string hoge = "test";
   Console.WriteLine(string.Format("「{0}」",test);

出力結果:「test」

  • 共用体を使う方法

CのunionをC#でも使えるみたいなので。
同じメモリ領域を複数の型が共有出来るので、とても便利。

   using System.Runtime.InteropServices;

   [StructLayout(LayoutKind.Explicit)]
   public struct UnionData
   {
       //0バイト目
       [FieldOffset(0)]
       public byte zero;

       //1バイト目
       [FieldOffset(1)]
       public byte one;

       //2バイト目
       [FieldOffset(2)]
       public byte two;

       //3バイト目
       [FieldOffset(3)]
       public byte three;

       //float型
       [FieldOffset(0)]
       public float floatData;

       //int型
       [FieldOffset(0)]
       public int intData;
   }   

共用体を使用したサンプル

       //共用体の宣言
       UnionData data = new UnionData();

       //int型にのみ値を代入
       data.intData = 1094861636;
       
       //それぞれの値を出力
       
       //0バイト目
       Console.WriteLine(Convert.ToChar(data.zero));

       //1バイト目
       Console.WriteLine(Convert.ToChar(data.one));

       //2バイト目
       Console.WriteLine(Convert.ToChar(data.two));

       //3バイト目
       Console.WriteLine(Convert.ToChar(data.three));

       //int型
       Console.WriteLine(string.Format("int = {0:d}",data.intData));

       //float型
       Console.WriteLine(string.Format("float = {0:0.000000}", data.floatData));

出力結果
出力サンプル.png

おわりに

まだまだ、わからないことや知らないことが多いので、勉強しないといけないです…。
データベースとかも勉強したい…

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

Azure App Service Static Web Apps に Blazor WASM と Azure Functions をデプロイする

1 つ前の記事で Vue.js + Azure Functions(中身は express) を Azure App Service Static Web Apps (長いので以下 Static Web Apps) にデプロイしてみました。そして気づいたのですが C# で SPA を開発するための Blazor WebAssembly が GA してました!!アツイ!!

ただ、これは .NET Core 3.1 (LTS) で動くけど Blazor WASM は LTS じゃない点が注意ですね。

.NET 5 が出たら .NET 5 に移らないとサポートが切れちゃう。あと .NET 5 も LTS じゃないので .NET 6 が出たら .NET 6 に行かないといけないはず。.NET 6 は LTS の予定。

まぁそれは置いといて、つまり Static Web Apps に Blazor WASM を置けるかもということです。API も Azure Functions でまとめてデプロイ出来ていい感じ。ただ、2020/05/20 時点の Public Preview の Static Web Apps は Azure Functions のランタイムが node.js 固定っぽい?ので、Azure Functions 側は JavaScript か TypeScript で作らないといけないみたいです。ちょっと残念。(記事書きながら C# で進めてたらデプロイのところでダメだと気づいて記事を書きなおしたりしてる)

やってみよう

ということで Blazor WASM + Azure Functions (TypeScript) を Static Web Apps にデプロイしてみようと思います。

適当なフォルダー(私は C:Labs\BlazorStaticWebApps というフォルダー使いました)で以下のコマンドを打ちます。

> dotnet new blazorwasm -o Client -n StaticWebApps.Client
> mkdir Server
> cd Server
> func init --language typescript --worker-runtime node

これでクライアントとサーバーの両方のプロジェクトが出来ました。クライアントが Blazor WebAssembly でサーバーが C# の Azure Functions です。

次のコマンドをうって、サーバーの方に GetMessage 関数を作りましょう。

> func new -l typescript -n GetMessage -t HttpTrigger

デフォルトで以下のような GET と POST を受け取り name パラメーターか POST の場合は Body の JSON の name の値を元にメッセージを返す関数が作られます。

GetMessage.ts
import { AzureFunction, Context, HttpRequest } from "@azure/functions"

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    const name = (req.query.name || (req.body && req.body.name));

    if (name) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

export default httpTrigger;

今回はサーバー側はこれをそのまま使いましょう。次はクライアント側です。

Index.razor を開いて GetMessage 関数を叩くようにします。これも非常にシンプルですが、こんな感じで。

Index.razor
@page "/"
@using Microsoft.Extensions.Configuration
@inject HttpClient Http
@inject IConfiguration Configuration

<h1>Hello, world!</h1>

Welcome to your new app.

<p>@Message</p>

@code {
    private string Message { get; set; }
    protected override async Task OnInitializedAsync()
    {
        var res = await Http.GetAsync($"{Configuration.GetValue<string>("API")}/GetMessage?name=BlazorWASM");
        Message = res.IsSuccessStatusCode ?
            await res.Content.ReadAsStringAsync() :
            "Failed";
    }
}

API の呼び先は、構成ファイルから読むようにしました。Blazor WASM の単体で実行するときには、特に Proxy を設定するような項目はなさそうなので、開発時はローカルの Azure Functions を呼んで、本番は自分と同じドメインのやつを呼ぶようにしました。

ということで、wwwroot の下に appsettings.jsonappsettings.Development.json を置いて以下のような内容にします。

appsettings.json
{
    "API": "/api"
}
appsettings.Development.json
{
    "API": "http://localhost:7071/api"
}

そして、Server 側の local.settings.json に CORS の設定を追加します。

local.settings.json
{
    "IsEncrypted": false,
    "Values": {
        "FUNCTIONS_WORKER_RUNTIME": "node",
        "AzureWebJobsStorage": "UseDevelopmentStorage=true"
    },
    "Host": {
        "CORS": "*",
        "CORSCredentials": false
    }
}

Server 側を起動させましょう。以下のコマンドでビルドして実行できます。

> npm install
> npm run build
> func host start

そして、クライアント側をデバッグ実行すると…

image.png

うまくいきましたね!!

Static Web Apps にデプロイ

じゃぁデプロイしてみましょう。とりあえず GitHub にソースを push します。ここにしました。

https://github.com/runceel/staticwebapps_blazor_test

とりあえず、こんな感じでビルドの設定はしました。このまま作成して作られる GitHub Actions ではエラーになるので、まぁとりあえずひな型作ってくれるくらいの気持ちで入れました。

image.png

GitHub Actions に移動して YAML にちょっと追記します。

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
    - master
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
    - master

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.300
    - uses: actions/checkout@v1
    - name: Build Client
      run: dotnet publish ./Client/StaticWebApps.Client.csproj -c Release -o dist/website
    - name: Build And Deploy
      id: builddeploy
      uses: Azure/static-web-apps-deploy@v0.0.1-preview
      with:
        azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RED_SMOKE_0EB760D00 }}
        repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
        action: 'upload'
        ###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
        app_location: 'dist/website/wwwroot' # App source code path
        api_location: 'Server' # Api source code path - optional
        ###### End of Repository/Build Configurations ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
    - name: Close Pull Request
      id: closepullrequest
      uses: Azure/static-web-apps-deploy@v0.0.1-preview
      with:
        azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RED_SMOKE_0EB760D00 }}
        action: 'close'

追加したのは .NET Core 3.1.300 を入れるステップと、Blazor WASM のプロジェクトをビルドして静的 Web サイトとしてデプロイ出来るファイルを生成するステップです。
そして、Static Web Apps へのデプロイのための設定の app_location に dotnet publish したフォルダーにある wwwroot を設定します。api_location は TypeScript の場合も自動でビルドしてデプロイしてくれるみたいなので、素直に Server と指定するだけで大丈夫でした。

GitHub Actions が成功したのを見届けて Static Web Apps の URL を開いてみると、ちゃんと動いてました!GetMessage API も、ちゃんとクラウドのを叩いてるのがわかりますね。

image.png

まとめ

ということで、Blazor WASM の正式版がリリースされて、さらに Azure App Service Static Web Apps も public preview になってたのでデプロイだけしてみました。
API は、今のところ node の Azure Functions じゃないといけないみたいなのですが、GA までにはランタイムが選べるようになると嬉しいなぁ。

後、純粋に Blazor WASM を API と共に開発してデプロイするなら ASP.NET Core でホストするプロジェクトテンプレートで作って、ASP.NET Core で API を作って Azure App Service の Web Apps にデプロイするのが一番楽だと思うということを最後に書いておこうと思います。

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

【C#】 short型のerror CS0266について

自己紹介

こんにちは。tetraです。
最近C#の学習を始めました。C#歴2ヶ月目の新卒エンジニアです。

error CS0266について

学習課題としてコードを書いていた時にハマったポイントについて
Tipsを書いていこうと思います。

発生例

簡単な例として、以下のようなコードを書きます。

CSwork.cs
// x + y を計算するコード
using System;
public class main
{
    public static short calc(short x, short y)
    {
        return x + y;         //error CS0266
    }
    public static void Main()
    {
        Console.WriteLine(calc(1, 2));
    }
}

この場合、short型で宣言しているにもかかわらず、int型で処理されてしまいます。

解決法

これの処理の仕方を以下のコードで書きます。

CSwork2.cs
// x + y を計算するコード
using System;
public class main
{
    public static short calc(short x, short y)
    {
        return (short)(x + y);         
    }
    public static void Main()
    {
        Console.WriteLine(calc(1, 2));    //3
    }
}

どうでしょうか?変わっていないですか?
よくみてください。
calc関数のところがカッコ()で囲まれshort型として明示的に書かれています。

こうすることによって、short型の戻り値を持つ関数を処理することができるようになります。

いかがでしょうか?

いかがでしょうか。経験が浅いため至らぬところがまだまだあります。
もし間違い等に気がつきましたら、コメント又はtetraまでお知らせください。

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

C#バグ奮闘記 1日目

どうも、べです。
プログラミングの知識はほぼ皆無です。 今はC#でテトリスが作れるようになることが目標です。コロナで自宅謹慎、今後仕事で使うであろうC#を独学する日々です。意見やアドバイスなどコメント頂けると嬉しいです。

C#の自己紹介のようなテキストを一通り終えたので、実際にプログラムを作ってみようと思い、マイクロソフト公式のチュートリアル↓をやっています。
https://docs.microsoft.com/ja-jp/previous-versions/visualstudio/visual-studio-2013/dd492171%28v=vs.120%29

Visual BasicとC#の2つの言語のどちらかで3つの簡単(多分?)なプログラム(ピクチャビューア、計算クイズ、絵合わせゲーム)を作ってみようってやつです。
マイクロソフト公式だし、あまり楽しさは期待していませんでしたがやってみると結構面白い。計算クイズなんかは作っていて、2ケタx2ケタも30秒以内に計算するとか考えたら結構むずくね?とか色々考えてしまいます。

ページ内でVBとC#のコードの切り替えができる?っぽいのですが僕はそのやり方が分からないので言語を変換してくれるオンラインページ↓を使っていました。
https://converter.telerik.com/

それで、説明が丁寧なので特に問題なく来ていましたが、3つ目の絵合わせゲームで初めてバグと戦うことになりました。
コメントで//xってしているのがバグ↓の発生した部分です。

CS0825 C# The contextual keyword 'var' may only appear within a local variable declaration or in script code

「そんなとこにvar使わんといてくれ」みたいなことを言われている気がします。Listオブジェクト使うときには型を指定しないといけない?んですかね。
ちょっとネットで調べてみて↓、//xの下にあるように修正したら直りました(これが何を意味するのかはよく分からない)。
https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers

public partial class Form1 : Form
{
    //random icons
    private Random random = new Random();

    //icons list of webdings font
    //x        private var icons = new List<string>()
    //x       { "!", "!", "N", "N", ",", ",", "k", "k","b", "b", "v", "v", "w", "w", "z", "z" };

    private List <string> icons = new List<string>
    { "!", "!", "N", "N", ",", ",", "k", "k","b", "b", "v", "v", "w", "w", "z", "z" };

また、Listオブジェクトを呼び出す時にもバグ↓が発生しました。

CS1955 C# Non-invocable member cannot be used like a method.

よく分からないけど、使い方を間違ってるよーと言われている気がします。これは以下のサイト↓のように、配列と同じように[]でくくってあげることで解決しました。
https://programming.pc-note.net/csharp/list.html

    private void AssignIconsToSquares()
    {
        //pull icon from the list and add to label
        foreach(var control in tableLayoutPanel1.Controls)
        {
            var iconLabel = control as Label;
            if (iconLabel != null)
            {
                var randomNumber = random.Next(icons.Count);
                //x iconLabel.Text = icons(randomNumber);
                iconLabel.Text = icons[randomNumber];
                //iconLabel.ForeColor = iconLabel.BackColor;
                icons.RemoveAt(randomNumber);
            }
        }
    }

最後の勝利判定では本文の中にもcongraturations!を入れたかったので改行したく、C#では\rだけで改行できるということを調べて知りました↓。
https://dobon.net/vb/dotnet/string/newline.html

    private void CheckForWinner()
    {
        //go through all the labels
        //check all matched
        foreach (var control in tableLayoutPanel1.Controls)
        {
            var iconLabel = control as Label;
            if (iconLabel != null && iconLabel.ForeColor == iconLabel.BackColor)
            {
                return;
            }
        }

        //if the loop didn't return,
        //it did't find any unmatched icons
        //show a message "user won"
        MessageBox.Show("You matched all the icons! \r congraturations!(-ω☆)キラリ", "congraturations!");
        Close();
    }

とりあえずこんな感じで楽しいゲームができました。
素朴なゲームですけど、自分で作るとすごく楽しく感じますよね。
絵文字の代わりに用意した画像で絵合わせしたり、手数制限を加えたりするともっと面白くなると思いました。

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

【Unity:C#】null条件演算子(?.)

null条件演算子
if文書かずに1行ですっきりするから結構使用してるんだけど

sample
Action callback;
callback?.Invoke();

Unityのオブジェクトに関しては使用してはいけない

sample
// 例として面倒だから定義直下で判定してるけどメンバ変数として持って使用してる場合を想定
GameObject gobj;
gobj?.name;

明示的にnull代入すればセーフだけど
どこかでGameObjectの参照が切れた場合
gobjには"null"が入ってる
nullではなくて"null"

これはnull条件演算子が対応してないので普通にnull参照でエラーになる
※厳密には「== null」での判定はUnity側がoperatorを拡張してくれているから判定できるらしい(そのうち?.も対応されるかも??)

Unityのオブジェクトには普通にnullチェックしよう

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

【Unity】ゲーム全体でデータを共有する方法【プレイヤーのHPとか】

筆者について

Unityは初心者。
C#は仕事で4年使ってます。

どういう記事?

色々なオブジェクトから参照される数値… 例えばプレイヤーのHP。
体力バーを表示したり、敵のAIに反映したり。

他にも、ゲーム全体の進捗率や、フラグなんかもほしいかもしれませんね。

どのように共有するか、悩みませんか?
お勧めの方法があります!:gift:

結論

Scriptable Objectがオススメです!
:thinking:ん?Scriptable Object? マスタ設定用機能では?

私もそう思っていました。
ただのマスタデータ設定用機能じゃないんです。

利点

依存関係が綺麗に整理できる
通常のスクリプトより軽い(コールバック少)
シーンを切り替えても値が引き継がれる

欠点

ScriptableObjectの特性を把握する必要があります。

※たとえば、スクリプトから値を修正すると、ゲームの再起動で値は元に戻りますが、
インスペクタから直接値を変えると、永続的に値が変わります。

全て紹介すると普通に記事が一本かけてしまうので、詳細は公式や他の記事にお譲りしたいと思います。

他の方法はダメ?

例としてプレイヤーのHPの場合

普通に作るとプレイヤースクリプトへの参照を保持する方法になると思いますが、
無駄な依存関係を生むのでお勧めしません。
HPだけでいいのに、ゲームオブジェクトへの参照やスクリプトへの参照が必要ですよね。
FindしたりGetComponentしたり、処理速度的にも不利だと思います。

その他、ScriptableObjectよりも良い方法があれば、ぜひ教えてください!

やり方

前置きが長くなりました。すみません。
いよいよ具体的な使い方です!

① Scriptable Objectを定義

普通にScriptを定義する時と同じような手順でOK!
※ScriptableObjectを継承します

GlobalFloat.cs
using UnityEngine;

[CreateAssetMenu]
public class GlobalFloat : ScriptableObject
{
    public float Value;
}

上記例では「Value」1つですが、複数のシリアライズフィールドを作成してももちろんOKです。

② Scriptable Objectを作成

さきほど [CreateAssetMenu] 属性を指定しているので、
プロジェクトウィンドウ内で右クリック ⇒ Create ⇒ Global Float で作成できると思います!
image.png

※Resourcesと名の付くフォルダ下に作るとスクリプトからロードできて便利
使っちゃダメらしいんですが便利なんだもん
image.png

③ インスペクターから設定 or リソースとして読み込みます。

まず、読み取ったり書き込んだりしたいスクリプトにさきほど定義した型のメンバを定義しましょう。

PlayerController.cs
using UnityEngine;

[CreateAssetMenu]
public class PlayerController : MonoBehaviour
{
    public GlobalFloat HP;
}

インスペクタから設定する場合は、いつも通りドラッグアンドドロップ!

image.png

リソースを読み込む場合(建前上使っちゃダメ)

PlayerController.cs
void Start()
{
    HP = Resources.Load<GlobalFloat>("PlayerHP");
}

どちらでもOKです!
他のクラスでも同じ共有変数にアクセスする場合、手順③を再度適用しましょう。

④ 読み取る / 書き込む

メンバのValueプロパティが共有データとなります!

PlayerController.cs
// 読み取り
Debug.Log(HP.Value);
// 書き込み
HP.Value = 100f;

参考

ScriptableObject を使用してゲームを構築する 3 つの方法
※Unity公式HowToの変数の設計と同じ内容です。

初見では、具体的な使い方がよく分からなかったので、
私なりにかみ砕き、スクリーンショットを添えて記事にしました。
次回は、同じ記事元の「イベントシステム」をご紹介したいと思います。

皆様のお役に立ちますように!

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