- 投稿日:2020-03-23T23:57:01+09:00
Roslynと複数形の不規則変化
C#の命名規則
C#ではMicrosoftの指針(識別子名 | Microsoft Docsや大文字の仕様規則)に従って名前を付けることが多い。
今回の話題に関わる点だけ挙げると
- 型名はPascalCase
- 変数名はcamelCase
という規則は一般に使われていると思う。
変数名サジェスト
C#で開発しているとVisualStudioでもVSCode&OmniSharpでもRoslynによる優秀なインテリセンスが使用できる。
このインテリセンスには型名から変数名を提案する機能があり、変数名としてその変数の型名をPascalCaseからcamelCaseに変換したもの、あるいは型名をPascalCaseに従って分離したうえで一部を取り出してcamelCaseにしたものを提案してくれる。
具体的には以下のような提案が現れる。//型名はPascalCase class SpecialRapidServiceTrain { } //この型の変数名として //型名のPascalCaseをcamelCaseに変換したもの private SpecialRapidServiceTrain specialRapidServiceTrain; //型名から一部を取り出してcamelCaseにしたもの private SpecialRapidServiceTrain rapidServiceTrain; private SpecialRapidServiceTrain serviceTrain; private SpecialRapidServiceTrain train; private SpecialRapidServiceTrain special; private SpecialRapidServiceTrain specialRapid; private SpecialRapidServiceTrain specialRapidService; //が提案されるまたこの機能はある型のコレクションに対しては型名の複数名のcamelCase、あるいは変数名と同様に型名の一部を複数形にしたcamelCaseを提案する。
//型名を複数形のcamelCaseに変換したもの private SpecialRapidServiceTrain[] specialRapidServiceTrains; //型名を分離した上で一部を取り出して複数形のcamelCaseにしたもの private SpecialRapidServiceTrain[] rapidServiceTrains; private SpecialRapidServiceTrain[] serviceTrains; private SpecialRapidServiceTrain[] trains; private SpecialRapidServiceTrain[] specials; private SpecialRapidServiceTrain[] specialRapids; private SpecialRapidServiceTrain[] specialRapidServices; //が提案される。本題 : 不規則変化の名詞
ここからが本題。実はこの機能は不規則変化する名詞の複数形にも対応している。
分かりやすいところで言うと次のようなものがある。//型SmallChildのコレクションに対して…… private SmallChild[] smallChildren; //型LegalPersonのコレクションに対して…… private LegalPerson[] legalPeople;じゃあこれはいったいどのくらいの不規則変化名詞に対応しているのか?
以下、不規則変化する名詞一単語からなる型名のコレクションに対してどのような変数名がサジェストされたかをまとめる。英単語のリストは#3586. 外来複数形[plural][latin][greek][french][italian]をもとにした。なお、このリストは外来複数形のものなので英語の古い形に由来する不規則変化は載っていない。
正しく不規則変化されたもの
単数形 複数形 意味 サジェスト bacillus bacilli バチルス菌 bacilli stimulus stimuli 刺激 stimuli alga algae 藻 algae alumna alumnae 女子卒業生 alumnae larva larvae 幼虫 larvae addendum appenda 付録 appenda bacterium bacteria バクテリア bacteria datum data データ data erratum errata 誤植 errata codex codices 古写本 codices analysis analyses 分析 analyses axis axes 軸 axes basis bases 理論的基礎 bases oasis oases オアシス oases parenthesis parentheses 丸かっこ parentheses synopsis synopses 概要 synopses synthesis syntheses 総合,合成 syntheses criterion criteria 判断の基準 criteria phenomenon phenomena 現象 phenomena corps corps(単複同形) 団 corps rendezvous rendezvous(単複同形) 約束による会合 rendezvous 使用頻度の低そうな語にも対応していることに驚かされる。
規則変化と不規則変化両方がある単語
不規則変化が採用されたもの
単数形 複数形 意味 サジェスト cactus cacti, cactuses サボテン cacti radius radii, radiuses 半径 radii syllabus syllabi, syllabuses 教授細目 syllabi vertebra vertebrae, vertebras 脊椎 vertebrae aquarium aquaria, aquariums 水族館 aquaria curriculum curricula, curriculums 教育課程 curricula memorandum memoranda, memorandums メモ memoranda symposium symposia, symposiums シンポジウム symposia apex apices, apexes 頂点 apices index indices, indexes 指標 indices matrix matrices, matrixes 行列 matrices サボテンが登場するコードでも安心だ。
規則変化が採用されたもの
単数形 複数形 意味 サジェスト antenna antennae, antennas antennae (触覚), antennas (アンテナ) antennas formula formulae, formulas 公式 formulas nebula nebulae, nebulas 星雲 nebulas spectrum spectra, spectrums スペクトル spectrums automaton automata, automatons 自動装置,ロボット automatons ganglion ganglia, ganglions 神経節 ganglions bureau bureaux, bureaus 事務局 bureaus plateau plateaux, plateaus 高原 plateaus tableau tableaux, tableaus 絵 tableaus libretto libretti, librettos 脚本 librettos tempo tempi, tempos テンポ tempos virtuoso virtuosi, virtuosos 巨匠 virtuosos フランス語・イタリア語由来の語はどれも規則変化が採用されているようだが理由があるのだろうか?
誤って活用されたもの
単数形のままcamelCaseにされたもの
単数形 複数形 意味 サジェスト esophagus esophagi 食堂 esophagus cirrus cirri, cirruses 巻雲 cirrus genius genii,geniuses genii (守護神),geniuses (天才) genius nimbus nimbi, nimbuses 乱雲 nimbus corpus corpora, corpuses 集大成,言語資料 corpus genus genera, genuses 属 genus corpusやgenusなどは使いどころも多そう(少なくともalumnaよりはコード中に出てくるだろう)なのに誤った形が提案されている。
どの語も末尾がsで終わっていることから考えるとRoslynが単数形を規則変化の複数形と誤認している疑いがある。そうだとすると「正しく不規則変化されたもの」に挙げたcorpsやrendezvousも単複同形だったためたまたま正しい結果が出ただけで、Roslyn側は謝って複数形の名詞と解釈しているのかもしれない。実際、型名が複数形だと//型名Booksは複数形だと判断しているらしい private Books[] books; //型名Childrenは複数形ではないと判断している? private Children[] childrens;のような提案がなされるためだ。
存在しない複数形が提案されたもの
単数形 複数形 意味 サジェスト chassis chassis(単複同形) 自動車の車台,シャシー chasses これは不思議なパターンだ。本来存在しないはずの不規則変化の複数形*chassesがサジェストされている。(Weblioではハイパー英語辞書がこの形をchassisの複数形としているが、他の辞書による裏付けがないので疑わしい。もしOEDをお持ちの方がいたらこの形が載っているか調べていただきたい。自分でやればいいのだが図書館が閉館中なので調べにいけない……)
実装
RoslynのDeclarationNameCompletionProvider.csがこれらの処理に対応する実装のはず。
ただ肝心の名詞リスト(おそらく単数形から複数形を引ける辞書のようなものがあるはず)を見つけられていない。追記
EnglishPluralizationService.cs - ReferenceSourceに処理を発見した。これだろうか?予想通り辞書の形となっている。まとめ
Roslynの変数名サジェスト機能は不規則変化する名詞にそれなりに対応しているが、漏れもあるので過信してはいけない。
規則変化と不規則変化どちらの複数形もある名詞に対しては一方の形にしか対応できていないと思われるが、どちらの形の複数形を採用するかの方針はよくわからない。
- 投稿日:2020-03-23T23:42:46+09:00
Unityで簡単なゲームを作ってみた
作成したゲーム
上から降ってくる岩を打ち落として、得点を稼ぐシューティングゲーム。岩が画面外に出てしまったらゲームオーバー。
Unityのバージョンは2018.4.19f1
プレイヤー移動はキーボード入力
弾が岩に当たったら爆発のエフェクト
弾を発射した際、Trail Rendererを使って弾の軌跡を出す
UIで得点を表示し、岩が画面外に行ったときにゲームオーバーを表示を出す
用意したもの
- プレイヤー、障害物(岩とか)、弾、弾の軌跡、背景
ソースコード
プレイヤー(ロケット)を動かすスクリプト
RocketController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class RocketController : MonoBehaviour { public GameObject bulletPrefab; void Update() { if (Input.GetKey(KeyCode.LeftArrow)) { transform.Translate(-0.1f, 0, 0); } if (Input.GetKey(KeyCode.RightArrow)) { transform.Translate(0.1f, 0, 0); } if (Input.GetKeyDown(KeyCode.Space)) { Instantiate(bulletPrefab, transform.position, Quaternion.identity); } } }Instantiate関数
Instantiate(bulletPrefab,transform.position,Quaternion.identify); //Instantiate(第1関数,第2関数,第3関数);
- この関数ではPrefabからインスタンスを作り、任意の位置に生成する関数です
- 第1関数にPrefab、第2関数にインスタンスを作成したい位置、第3関数にはインスタンスの回転角を指定します
- 今回は弾のPrefab(bulletPrefab)を複製して発射したいのでこれを第1関数に、これをプレイヤーの位置に生成したい ので第2関数はtransform.position、第3関数の回転はQuaternion.identifyにすることで無回転になります
岩の制御
- 岩を落とすスクリプト
RockController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class RockController : MonoBehaviour { float fallSpeed; float rotSpeed; void Start() { this.fallSpeed = 0.01f + 0.1f * Random.value; this.rotSpeed = 5f + 3f * Random.value; } void Update() { transform.Translate(0, -fallSpeed, 0, Space.World); transform.Rotate(0, 0, rotSpeed); if (transform.position.y < -5.5f) { GameObject.Find("Canvas").GetComponent<UIController>().GameOver(); Destroy(gameObject); } } }Translate関数
transform.Translate(0, -fallSpeed, 0, Space.World); //transform.Translate(x, y, z, relative to);
- 最初の3つの値はxyzどの軸に沿って移動するかという意味で、最後のrelative toにはSpace.WorldやSpace.Selfなどが入ります。Space.Worldにした場合、ワールド座標になります。ワールド座標とは原点から見た座標のことです。
- 今回の場合、fallSpeedに従って落ちてきてほしいのでy軸のみ値が入っています
- ランダムに岩を生成するスクリプト
RockGenerator.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class RockGenerator : MonoBehaviour { public GameObject rockPrefab; void Start() { InvokeRepeating("GenRock", 1, 1); } void GenRock() { Instantiate(rockPrefab, new Vector3(-2.5f + 5 * Random.value, 6, 0), Quaternion.identity); } }InvokeRepeating関数
InvokeRepeating("GenRock", 1, 1); //InvokeRepeating(第1関数, n, m);
- この関数はとても便利な関数です。第1関数に指定したものを、n秒後にm回呼び出す関数です。
- 今回はGenRockという関数を1秒に1回呼び出したいのでこのようになります
背景を動かすスクリプト
BackgroundController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class BackgroundController : MonoBehaviour { void Update() { transform.Translate(0, -0.03f, 0); if (transform.position.y < -4.9f) { transform.position = new Vector3(0, 4.9f, 0); } } }
- 背景の大きさは決まっているのである一定のところまで移動したら、戻るようにif文で設定しています
発射する弾の制御
BulletController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class BulletController : MonoBehaviour { public GameObject explosionPrefab; //爆発エフェクトのPrefab void Update() { transform.Translate(0, 0.2f, 0); if (transform.position.y > 5) { Destroy(gameObject); } } void OnTriggerEnter2D(Collider2D coll) { //衝突したときにスコアを更新する GameObject.Find("Canvas").GetComponent<UIController>().AddScore(); //爆発エフェクトを生成 GameObject effect = Instantiate(explosionPrefab, transform.position, Quaternion.identity) as GameObject; Destroy(effect, 1.0f); Destroy(coll.gameObject); Destroy(gameObject); } }ややこしい部分の解説
//爆発エフェクトを生成 GameObject effect = Instantiate(explosionPrefab, transform.position, Quaternion.identity) as GameObject; Destroy(effect, 1.0f);
- Instantiate関数でGameObjectとしてキャストすることで、爆発エフェクトを加える
- その後のDestroy関数でそのオブジェクトを消す。第2引数はオブジェクトを破壊するまでの時間(秒)です
UIの制御
UIController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class UIController : MonoBehaviour { int score = 0; GameObject scoreText; GameObject gameOverText; public void GameOver() { this.gameOverText.GetComponent<Text>().text = "GameOver"; } public void AddScore() { this.score += 10; } void Start() { this.scoreText = GameObject.Find("Score"); this.gameOverText = GameObject.Find("GameOver"); } void Update() { scoreText.GetComponent<Text>().text = "Score:" + score.ToString("D4"); } }スコアの更新
scoreText.GetComponent<Text>().text = "Score:" + score.ToString("D4");
- UIのテキスト部分に「Score:0000」と表示するためにscoreText.GetComponent.textで情報を入手する
- score.ToString("D4")は整数を4桁表示するという意味です
作成したゲーム
問題点
- 同じ場所で弾を連射すると、弾同士がぶつかってしまいそこに爆発エフェクトがでてしまう。
参考させていただいたサイト
追記
- GitHubにコード上げました!
- 投稿日:2020-03-23T19:37:28+09:00
C# を触ってみたいけど自分の環境に .NET の SDK 入れるの何か嫌だという人向けハローワールド
以下の記事が思ったよりバズっててびっくりしたので気をよくして、これをきっかけに C# ちょっと気になるなという人向けの最初の、本当に最初に触ってみるための一歩を書いてみようと思います。
コンソールアプリをブラウザーでお試し
この記事を書いた後に岩永さんから教えてもらいました。(知らなかったです)
https://t.co/0y9jbUSEHq とか https://t.co/DAYydYlKcS とか<SDK入れるの嫌な人向け
— ++C++; // 管理人: 岩永 (@ufcpp) March 23, 2020Try .NET というのがあるんですね。補完も効いていい感じ。
英語ですが、これを使ったチュートリアルもありました。
Hello World - Introduction to C# interactive C# tutorial
本当にコンソールアプリで触ってみたいとかなら、これでもよさそう。
ローカルの VS Code で試してみたくて Web アプリも試したいという人は以下の VS Code + リモート開発をどうぞ。
Docker と VSCode を入れる
大体の人が既に入れてるよね?今回は、以下の 2 つが入ってることを前提に進めます。
VSCode のリモート開発拡張機能を入れる
まだ、プレビューなんですが Visual Studio Code のリモート開発拡張機能を入れます。
これを入れると Docker コンテナに繋いでホストマシンの VS Code でコード書いたりできます。
Hello world
- 適当な空のフォルダーを VS Code で開きます
- コマンドパレットから
Remote-Containers: Add Development Container Configuration Files...
を叩きます- 色々な言語などのリストが出てくるので C# .NET Core 3.1 を選びます
- リモート開発用の構成ファイルが追加されます。
.devcontainer/devcontainer.json
の"extensions":
に"ms-vscode.csharp"
とありますが、これを"ms-dotnettools.csharp"
に変更します。
コマンドパレットから
Remote-Containers: Reopen in Container
を実行します
- 初回は docker pull とか走るので時間がかかります。
しばらくすると、コンテナーが立ち上がって VS Code のリモート開発機能でつながった状態になります
ターミナルで
dotnet -version
とすると以下のような結果になるはずです。root@7d5755714aa7:/workspaces/VSCodeCSharp# dotnet --version 3.1.100
- ターミナルで
dotnet new console -o HelloWorld
と打ち込んでプロジェクトを生成します。- ターミナルで
code -r HelloWorld
を入力して HelloWorld フォルダーを開きます。- コマンドパレットから
.NET: Generate Assets for Build and Debug
を実行するとデバッグやビルドに必要なtasks.json
やlaunch.json
が追加されます。- この状態で、F5 を押すとデバッグできます。
後は、C# を勉強するだけ!参考サイトは公式と、鉄板のサイトとしては岩永さんのサイトがあります。
この他にも Microsoft Learn という自己学習用のサイトにも C# のコースがあります。
この Microsoft Learn の C# のラーニングパスの何処かに変数宣言での var は使うな的なことが書いてありますが、個人的には var 使う派なので、そこは許せない。(個人の感想)
Web アプリもいけます
このコンテナ上で Web アプリの開発とデバッグも行けます。試しに ASP.NET Core Razor Pages でやってみましょう。
コンテナの作業フォルダーのルートに戻って以下のコマンドを実行します。# dotnet new webapp -o HelloRazorそして
code -r HelloRazor
コマンドでフォルダーを開きましょう。コンソールの時と同じ手順でtasks.json
やlaunch.json
を追加します。そして、F5 で実行すると自動でポートフォワーディングの設定まで追加してくれて、ホストマシン側のブラウザーでコンテナー内の Web アプリが開きます。まとめ
Visual Studio Code のリモート開発機能を使えばサクッと C# を始めることが出来ます。
とりあえず自前環境に .NET Core SDK は入れたくないけど、C# ちょっと触ってみたいなという人向けの 1 つの選択肢としてどうぞ!
- 投稿日:2020-03-23T18:24:07+09:00
つくるオーオース WEBAPI編
MVCが出来たのでWEBAPIを実装する事で、
https://qiita.com/namikitakeo/items/0de598b8e43eb5b1ff94Client Credentials Grantと、
https://qiita.com/namikitakeo/items/0c283b2e5da55670c542Resource Owner Password Credentials Grantを再現しようと思います。
https://qiita.com/namikitakeo/items/ea23adbc0b5c941ff0edTokenコントローラーを作成します。
http://localhost:5000/op/tokenControllers/TokenController.csusing System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using myop.Models; namespace myop.Controllers { public class AccessToken { public string access_token { get; set; } public int expires_in { get; set; } public string token_type { get; set; } public string scope { get; set; } } [Route("op/[controller]")] [ApiController] public class TokenController : ControllerBase { private readonly myopContext _context; string CLIENT_ID; string CLIENT_SECRET; string GRANT_TYPE; string SCOPE; string USERNAME; string PASSWORD; public TokenController(myopContext context) { _context = context; } // POST: op/token [HttpPost] public async Task<ActionResult<AccessToken>> doPost() { string body = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); string[] p = body.Split('&'); for (int i=0; i<p.Length; i++){ string[] values = p[i].Split('='); switch(values[0]) { case "client_id":CLIENT_ID=values[1];break; case "client_secret":CLIENT_SECRET=values[1];break; case "grant_type":GRANT_TYPE=values[1];break; case "scope":SCOPE=values[1];break; case "username":USERNAME=values[1];break; case "password":PASSWORD=values[1];break; } } var client = await _context.Clients.FindAsync(CLIENT_ID); if (client == null) { return null; } if (client.GrantType != GRANT_TYPE) { return null; } string t="openid"; if (SCOPE != null) { string[] s = SCOPE.Split(' '); for (int j=0; j<s.Length; j++){ if (s[j]!="openid" && client.AllowedScope.Contains(s[j])) t=t+" "+s[j]; } } SCOPE=t; if (client.AccessType == "confidential") { if (client.ClientSecret != CLIENT_SECRET) return null; if (client.GrantType == "client_credentials") USERNAME="admin"; } else if (client.AccessType == "public") { if (client.GrantType != "password") return null; if (CLIENT_SECRET != null) return null; } else { return null; } if (client.GrantType == "password") { var user = await _context.Users.FindAsync(USERNAME); if (user == null ) return null; if (user.Password != PASSWORD) return null; } var token = await _context.Tokens.FindAsync(USERNAME); if (token != null) { _context.Tokens.Remove(token); await _context.SaveChangesAsync(); } string random = Guid.NewGuid().ToString("N").ToUpper(); token=new Token {Id = USERNAME, AccessToken = random, ExpiresIn=60, TokenType="bearer", Scope = SCOPE, Iat=DateTime.Now}; _context.Add(token); await _context.SaveChangesAsync(); AccessToken access_token=new AccessToken {access_token = random, expires_in=60, token_type="bearer", scope = SCOPE}; return access_token; } } }Client Credentials Grant動いているっぽい。
% curl -d "client_id=client2&client_secret=client2&grant_type=client_credentials&scope=openid address" http://localhost:5000/op/token {"access_token":"57E63D3C3E7040F98D9FA4A8CE55EB2D","expires_in":60,"token_type":"bearer","scope":"openid address"}Resource Owner Password Credentials Grant動いているっぽい。
% curl -d "client_id=client1&grant_type=password&username=user01&password=user01&scope=openid profile" http://localhost:5000/op/token {"access_token":"408AF2B56C064A228D6C341F01BC77EF","expires_in":60,"token_type":"bearer","scope":"openid profile"}
- 投稿日:2020-03-23T13:30:59+09:00
個人的に C# が向かないと思うこと
先日 @okazuki さんが C# で出来ること一覧 を書いていて、とても同意だった。C# 10年以上やってきて、その成長には満足しているし、対応ソリューションやプラットフォームは凄いものがあると思っています。
しかしこの記事では、あえて C# が逆に向かないことが何かを考えてみました。
あくまで個人の見解であり、同意できないことがあることは事前に同意してきます
C# Love なので、非常にバイアスがあります
他の言語そこまで知らないので、どの言語がいいという話はありません
普段 C# か Node.js で開発しているので、そもそも C# に向かないことを自分がやっていないだけというオチはあるかもHello World までが遠い
LINE イベントや外部ハッカソンなど、Microsoft の外で活動する際、C# は Hello World までの時間が Node.js や Python に比べて時間がかかります。理由はいくつかありますが、以下の通り:
IDE が必須
メモ帳でもできる!!とかいうのは詭弁(またはHENTAI)で、IDE 必須です。他の言語も同様であり、Visual Studio Code があるから良いよねという意見もありますが、本当に C# の威力を発揮するなら Visual Studio が欲しいので、ここで時間がかかります。VSCode もとても良いけどね!追記: Web API やコンソールアプリなど作るだけなら Visual Studio Code 最高!リアルタイムテストとかエミュレーターでごにょごにょやり出すと、VS 欲しい!です。
追記: そしてさらに、レベルがあがると VSCode すら不要だと。。。
「Hello World までが遠い」なるほど? pic.twitter.com/oLrdunclzm
— mattn (@mattn_jp) March 24, 2020Runtime
.NET Framework/.NET Core/Xamarin/Unity など用途によって色々あるのが強みでもあり、複雑さを増している感があります。.NET 5 が解決してくれることを期待しつつ。尚、耳に挟んだ情報では Python もたまに 2 か 3 か問題がある模様。コード量
最近は少なくなりましたが、Console.Write("Hello World");
以外のコードがそこそこあり、まだオブジェクト指向であるが故にスコープで失敗しがちです。これはオブジェクト指向言語全般に言えるでしょう。Windows
C# の問題ではありませんが、個人的にイベントやハッカソンで C# を選択する場合、Windows ユーザーが比較的多い場合で、ここにも隠れた課題が。
- まだ Windows 7 ですー (別にいいんだけど、ハードが弱いことも多い)
- 会社の PC で制限があってツールはいらなーい
- VPN とセキュリティソフトが悪さ?してー
などなど IDE 入れることすらままならない時が散見されます。
Hello World の後も壁がそこそこある
Hello World を超えて次に進撃した場合にも、C# ならではの壁があります。
VS のテンプレートがリッチ
これはとても良い事だけど、初心者に対する敷居をすこし上げている気もします。過保護かな。。Node.js/express のように、とりあえず get だけ書こうねーという気楽さが足りない一方で、ユニットテストとか初めからついてきて最高とも言えますが、作成直後、ユニットテストプロジェクトがまず消される事件現場に何度も遭遇したことがあります。サンプルが多様すぎる
C# は歴史は中堅どころですが、進化している言語なので、時期によりサンプルの書き方が大幅に異なります。これは他の言語でも当然言えるはずですが、数年離れると何書いてるかわからないから、ギャル語 (死語ですいません)のようだといわれたこともあります。(async/await、Linq、Generic、ラムダ、var の頃)ただし、他言語のいい所は取り入れているので記述方式などに特殊さはあまりないと思っています。
実行環境の壁がある
C# は色々できる反面、実行環境が (まだ?) 1 つではありません。.NET Core/Framework、UWP、WPF など、フレームワークや実行環境が異なると、コピペで動かないものもあり、混乱を呼びます。C# というキーワードだけでなく、ラインタイムを意識する必要があるので、コピペ職人泣かせです。(職人までくると逆に大丈夫か)
また同じ Windows アプリでもものによって使える API が異なるため、同じく動かないコードがあります。こちらも極力解消するよう Microsoft は日々頑張っています。データクエリ
実は Spark でも動く我らが C#! (.NET for Apache® Spark™ を参照)
そしてデータクエリは Entity Framework が(賛否両論含めて)ある!だが基本データ操作は SQL Server なら T-SQL がいいし、それぞれ専用のものがあるので、C# あればデータクエリも完璧だぜとは言い難いです。これは他の言語も同様だと認識してます。違ったら教えてください。
機械学習
ML.NET はじめました!なんですが、サンプルなど色々考えると、現状モデル読み込みと実行する時に、C# で出来るからアプリが C# でも安心!くらいの感じで、モデル開発中は Python やら R などをよく使います。サンプルも多いですし実行環境も多いので。この分野はこれからかな?
マイクロソフト製品が対応していない場所での利用
適材適所のため、MS もなんでもかんでも C#/.NET ではありません。Power BI のカスタムビジュアルや SharePoint Framework、PowerApps Component Framework などは Node.js です。そこに無理やり C# を使って Javascript コード生成をねじ込むのは、ベテランとツールを持っている人だけがやる事でしょう。
いまでは TypeScript もありますので、型問題はある程度解決です。
関数型プログラミング
C# はオブジェクト指向型のため向きません。F# をどうぞ。
スクリプト処理
Linux/Windows に限らず様々な自動化でスクリプトを書きますが、Windows であっても PowerShell で書きます。PowerShell は C# 実行できるとか、モジュールが C# で書ける!とかそういうのは関係なく、スクリプト処理に向く言語ではないと思います。
また az コマンドなど各種 CLI があるものは、当然そのまま使うので、わざわざ C# でラップして呼び出すようなことは(特に要件が無い限り)しません。
補足: dotnet-script なる C# でスクリプト実行する面白いライブラリがあることをコメントで教えていただきました。Azure Function ぽくていいかも!
そしてC#でもスクリプト実行したい!も併せてどうぞ。Arudino や micro:bit
C# をねじ込む必要はありません。専用の言語と IDE でどうぞ。
コンソール環境のみで開発
無理ゲーです。そんなことする理由は不明ですが、開発はコンソールでしかしない!vim 最高!と言われたら C# を推すことはしていません。
まとめ
とりあえず色々思いついたことをつらつらと書いてみましたが、基本的には短期イベントの際に、経験者が少ない場合はお勧めしてません。
もし向いてないリストをもっと昔に書いたら、リストはこんなものでは無かったでしょう。
モバイル開発できない -> Mono と Xamarin
Windows でしか動かない -> .NET Core
ブラックボックスだ -> オープンソース化
ビルドから実行が遅い -> Roslyn
C# はそのうち終わる -> いわれ続けてもうじき 20 年。ありがとう。
クライアントサイドがごにょごにょ -> Blazor
その他山ほどの不満 -> フィードバックとして日々進化結論: C# は投資に値する良い言語です。(趣旨と違
- 投稿日:2020-03-23T09:54:52+09:00
C#:列挙型の解説
Sample.csusing System.Collections; using System.Collections.Generic; using UnityEngine; // 列挙型とは? // * コードを読みやすくするためのもの(可読性をあげる) // * 制限の中から選択可能にするもの public class Sample : MonoBehaviour { enum DIRECTION { UP, DOWN, RIGHT, LEFT, } DIRECTION direction = DIRECTION.DOWN; int directionInt = 0; // 0:上 void Start() { if (directionInt == 0) { Debug.Log("下"); } else if(directionInt == 1) { Debug.Log("上"); } switch (direction) { case DIRECTION.DOWN: Debug.Log("下"); break; case DIRECTION.UP: Debug.Log("下"); break; case DIRECTION.RIGHT: Debug.Log("下"); break; case DIRECTION.LEFT: Debug.Log("下"); break; } /* if (direction == DIRECTION.Down) { Debug.Log("下"); } else if (direction == DIRECTION.Up) { Debug.Log("上"); } else if (direction == DIRECTION.RIGHT) { Debug.Log("右"); } else if (direction == DIRECTION.LEFT) { Debug.Log("左"); } */ } }
- 投稿日:2020-03-23T09:52:32+09:00
C#:2次元配列の解説
Sample.csusing System.Collections; using System.Collections.Generic; using UnityEngine; // 2次元配列 // * Table/表 public class Sample : MonoBehaviour { // 宣言方法 // 1次元配列 int[] arrayInt = new int[3]; // 2次元配列 int[,] tabelInt = new int[3, 2]; void Start() { // 代入方法 tabelInt[0, 0] = 1; tabelInt[2,1] = 10; // 取得方法 //int x = tabelInt[0, 0]; //長さ //Debug.Log(arrayInt.Length); Debug.Log(tabelInt.GetLength(0)); Debug.Log(tabelInt.GetLength(1)); for (int x = 0; x<tabelInt.GetLength(0); x++) { for (int y = 0; y < tabelInt.GetLength(1); y++) { Debug.Log(tabelInt[x,y]); } } for (int y = 0; y < tabelInt.GetLength(1); y++) { for (int x = 0; x < tabelInt.GetLength(0); x++) { Debug.Log(tabelInt[x, y]); } } } }
- 投稿日:2020-03-23T08:29:44+09:00
つくるオーオース MVC編
はじめに
この記事では.NET Core 3.1をつかってオーオースを学習する方法を書いてみます。
実行環境
下記バージョンで動作確認しています。
- MacOS
- .NET Core 3.1% dotnet --version 3.1.101学習方針
コマンドプロンプトから実行する事で、Windows、Linuxにおいてもそのままできると思います。
% mkdir myop % cd myop % dotnet new mvc必要なツールをインストールします。
% dotnet tool install --global dotnet-ef % dotnet tool install --global dotnet-aspnet-codegenerator % dotnet tool list --global必要なパッケージをインストールします。
% dotnet add package Microsoft.EntityFrameworkCore.Sqlite % dotnet add package Microsoft.EntityFrameworkCore.Design % dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design % dotnet add package Microsoft.EntityFrameworkCore.SqlServerClients、Tokens、Usersモデルを作成します。
Models/Tables.csusing System; using System.ComponentModel; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; namespace myop.Models { public class myopContext : DbContext { // public myopContext (DbContextOptions options) : base(options) {} public DbSet<Client> Clients { get; set; } public DbSet<Token> Tokens { get; set; } public DbSet<User> Users { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite("Data Source=myop.db"); } public class Client { [DisplayName("client_id")] public string ClientId { get; set; } [DisplayName("client_secret")] public string ClientSecret { get; set; } [DisplayName("access_type")] public string AccessType { get; set; } [DisplayName("redirect_uri")] public string RedirectUri { get; set; } [DisplayName("grant_type")] public string GrantType { get; set; } [DisplayName("allowed_scope")] public string AllowedScope { get; set; } } public class Token { [DisplayName("user_id")] public string Id { get; set; } [DisplayName("access_token")] public string AccessToken { get; set; } [DisplayName("expires_in")] public int ExpiresIn { get; set; } [DisplayName("token_type")] public string TokenType { get; set; } [DisplayName("scope")] public string Scope { get; set; } [DisplayName("iat")] public DateTime Iat { get; set; } } public class User { [DisplayName("user_id")] public string Id { get; set; } [DisplayName("password")] public string Password { get; set; } } }モデルからデータベースを生成します。今回データベースにはSQLiteを使います。
% dotnet ef migrations add InitialCreate % dotnet ef database updateテスト用なのでポート番号は5000のみで良いと思います。
Properties/launchSetting.json{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:38239", "sslPort": 44320 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "myop": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }スキャフォールドでデータベースを確認してみます。
% dotnet aspnet-codegenerator controller -name ClientsController -m Client -dc myopContext --relativeFolderPath Controllers --useDefaultLayout --referenceScriptLibraries % dotnet aspnet-codegenerator controller -name TokensController -m Token -dc myopContext --relativeFolderPath Controllers --useDefaultLayout --referenceScriptLibraries % dotnet aspnet-codegenerator controller -name UsersController -m User -dc myopContext --relativeFolderPath Controllers --useDefaultLayout --referenceScriptLibrariesソースコードを一部修正して実行します。これでClients、Tokens、UsersがMVCで登録できます。
http://localhost:5000/Clients/
http://localhost:5000/Tokens/
http://localhost:5000/Users/Startup.csusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using myop.Models; using Microsoft.EntityFrameworkCore; namespace myop { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<myopContext>(options => options.UseSqlite("Data Source=myop.db")); services.AddControllersWithViews(); } (省略)Models/Tables.csusing System; using System.ComponentModel; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; namespace myop.Models { public class myopContext : DbContext { public myopContext (DbContextOptions options) : base(options) {} public DbSet<Client> Clients { get; set; } public DbSet<Token> Tokens { get; set; } public DbSet<User> Users { get; set; } // protected override void OnConfiguring(DbContextOptionsBuilder options) // => options.UseSqlite("Data Source=myop.db"); } public class Client { [DisplayName("client_id")] public string ClientId { get; set; } [DisplayName("client_secret")] public string ClientSecret { get; set; } [DisplayName("access_type")] public string AccessType { get; set; } [DisplayName("redirect_uri")] public string RedirectUri { get; set; } [DisplayName("grant_type")] public string GrantType { get; set; } [DisplayName("allowed_scope")] public string AllowedScope { get; set; } } public class Token { [DisplayName("user_id")] public string Id { get; set; } [DisplayName("access_token")] public string AccessToken { get; set; } [DisplayName("expires_in")] public int ExpiresIn { get; set; } [DisplayName("token_type")] public string TokenType { get; set; } [DisplayName("scope")] public string Scope { get; set; } [DisplayName("iat")] public DateTime Iat { get; set; } } public class User { [DisplayName("user_id")] public string Id { get; set; } [DisplayName("password")] public string Password { get; set; } } }
- 投稿日:2020-03-23T06:20:13+09:00
UiPath Activity Creatorを使ってみる。(その5:スコープを持ったアクティビティの作成)
このドキュメントの概要
前回 はActivity Creatorでのアクティビティの外観の設定方法について見ていきました。
今回はスコープをもったアクティビティの作成方法について見ていきます。WizardでのScope指定
ActivityCreatorのWizardでACtivityの定義画面でType項目をScopeに設定するとScopeをもったアクティビティになります。また同一のWizard画面内でScopeを持ったアクティビティとSimple設定のアクティビティを作成すると、そのSimpleアクティビティはScopeアクティビティのChildアクティビティとして設定されます。
TestScopeアクティビティのプロパティを以下のように設定しました。
またTestChildアクティビティのプロパティを以下のように設定しました。
TestScopeアクティビティとして以下のコードが自動生成されます。
TestScope.csusing System; using System.Activities; using System.Threading; using System.Threading.Tasks; using System.Activities.Statements; using System.ComponentModel; using Company.Product3.Activities.Properties; using UiPath.Shared.Activities; using UiPath.Shared.Activities.Localization; namespace Company.Product3.Activities { [LocalizedDisplayName(nameof(Resources.TestScope_DisplayName))] [LocalizedDescription(nameof(Resources.TestScope_Description))] public class TestScope : ContinuableAsyncNativeActivity { #region Properties [Browsable(false)] public ActivityAction<IObjectContainer> Body { get; set; } /// <summary> /// If set, continue executing the remaining activities even if the current activity has failed. /// </summary> [LocalizedCategory(nameof(Resources.Common_Category))] [LocalizedDisplayName(nameof(Resources.ContinueOnError_DisplayName))] [LocalizedDescription(nameof(Resources.ContinueOnError_Description))] public override InArgument<bool> ContinueOnError { get; set; } [LocalizedDisplayName(nameof(Resources.TestScope_Str_DisplayName))] [LocalizedDescription(nameof(Resources.TestScope_Str_Description))] [LocalizedCategory(nameof(Resources.Input_Category))] public InArgument<string> Str { get; set; } // A tag used to identify the scope in the activity context internal static string ParentContainerPropertyTag => "ScopeActivity"; // Object Container: Add strongly-typed objects here and they will be available in the scope's child activities. private readonly IObjectContainer _objectContainer; #endregion #region Constructors public TestScope(IObjectContainer objectContainer) { _objectContainer = objectContainer; Body = new ActivityAction<IObjectContainer> { Argument = new DelegateInArgument<IObjectContainer> (ParentContainerPropertyTag), Handler = new Sequence { DisplayName = Resources.Do } }; } public TestScope() : this(new ObjectContainer()) { } #endregion #region Protected Methods protected override void CacheMetadata(NativeActivityMetadata metadata) { if (Str == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Str))); base.CacheMetadata(metadata); } protected override async Task<Action<NativeActivityContext>> ExecuteAsync(NativeActivityContext context, CancellationToken cancellationToken) { // Inputs var str = Str.Get(context); return (ctx) => { // Schedule child activities if (Body != null) ctx.ScheduleAction<IObjectContainer>(Body, _objectContainer, OnCompleted, OnFaulted); // Outputs }; } #endregion #region Events private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom) { faultContext.CancelChildren(); Cleanup(); } private void OnCompleted(NativeActivityContext context, ActivityInstance completedInstance) { Cleanup(); } #endregion #region Helpers private void Cleanup() { var disposableObjects = _objectContainer.Where(o => o is IDisposable); foreach (var obj in disposableObjects) { if (obj is IDisposable dispObject) dispObject.Dispose(); } _objectContainer.Clear(); } #endregion } }Childアクティビティへのデータの受け渡し
ScopeアクティビティからChildアクティビティへのデータの受け渡しは、ActivityCreatorで用意しているobjectContainerを利用します。
今回はScopeアクティビティにInArgument<string>
型のプロパティStrがありますので、これをChildアクティビティでも使えるようにします。具体的には以下のように _objectContainerにAddメソッドでデータを追加します。TestScope.cs(略) // Inputs var str = Str.Get(context); _objectContainer.Add(str); return (ctx) => { // Schedule child activities if (Body != null) ctx.ScheduleAction<IObjectContainer>(Body, _objectContainer, OnCompleted, OnFaulted); // Outputs }; (略)TestChild側ではobjectContainerインスタンスに対して
Get<T>
で値を取得します。
以下の例ではTestScopeアクティビティのプロパティとして取得した値の前に"000"を付加してOutStringプロパティとして返しています。TestChild.cs(略) protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken) { // Object Container: Use objectContainer.Get<T>() to retrieve objects from the scope var objectContainer = context.GetFromContext<IObjectContainer>(TestScope.ParentContainerPropertyTag); // Inputs var str = objectContainer.Get<string>(); /////////////////////////// // Add execution logic HERE /////////////////////////// str = "000" + str; // Outputs return (ctx) => { OutString.Set(ctx, str); }; } (略)動作確認のためUiPath Studioで以下のようなワークフローを作成してみます。
TestChildには入力プロパティがありませんが、TestScopeで入力した値を内部で参照しています。そのためTestScopeの入力で仮に"123"とした場合、strOutputには"000123"が返ってきます。
Wizard作成後に別のアクティビティをChildアクティビティにする方法
Scopeアクティビティを作成した後にChildアクティビティを追加したい場合があると思います。
この場合はWizardでTypeをSimpleにしたアクティビティを作成した後、以下の設定を加えることによりChildアクティビティにすることができます。
1. using UiPath.Shared.Activities.Utilities; の追加
2. コンストラクタにScope内でのみの利用制限の追加
3. 実行部分にobjectContainerの定義の追加後で追加したTestChild2アクティビティに対する具体的な変更個所は以下になります。
TestChild2.cs(略) using UiPath.Shared.Activities.Utilities; (略) #region Constructors public TestChild2() { Constraints.Add(ActivityConstraints.HasParentType<TestChild2, TestScope>(string.Format(Resources.ValidationScope_Error, Resources.TestScope_DisplayName))); } #endregion (略) // Inputs var objectContainer = context.GetFromContext<IObjectContainer>(TestScope.ParentContainerPropertyTag); (略)次回はnupkg周りの設定について説明します。
(その5 おわり)
- 投稿日:2020-03-23T02:30:31+09:00
再入門C#:デッドロック
コンカレントプログラミングの本を読んでいると、必ずデッドロックの話がでてくるので、実際にデッドロックするのか試したくなった。
デッドロックするコード
DeadLock() メソッドをシングルスレッドしか許されていない箇所から呼び出すとデッドロックが起こる。例えばWPFのUIスレッドから、これを呼び出すとデッドロックが起こる。理由は task.Wait() を呼び出すとこのスレッドのところで、待ちが発生する。一方、WaitAsAsync() メソッドの方では、await で、Task.Delay() を呼んでいる。await のセクションに入るときに現在のスレッドのコンテキストを保存する。await の箇所が終わると、そのコンテキストをリストアするのだが、その時にそのコンテキストは、task.Wait() によってロックがかかっているために、デッドロックになる。
private async Task WaitAsAsync() { await Task.Delay(TimeSpan.FromSeconds(1)); } private void DeadLock() { Task task = WaitAsAsync(); task.Wait(); }WPF アプリ
WPFは触ったことないけど、師匠のブログを読んで簡単なアプリを作ってみた。ボタンをおしていくと5回目で、Thread.Sleep() がかかり、10回目でこのデッドロックのロジックが呼ばれる。
MainWindow.xaml.cspublic partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new Prefecture(); } private async Task WaitAsAsync() { await Task.Delay(TimeSpan.FromSeconds(1)); } private void DeadLock() { Task task = WaitAsAsync(); task.Wait(); } private int count = 0; private void Button_Click(object sender, RoutedEventArgs e) { var button = (Button) sender; button.Content = string.Format($"{++count} times."); if (count == 4) { button.Content = string.Format($"Sleep 10 sec...."); } if (count == 4) { Thread.Sleep(TimeSpan.FromSeconds(10)); } if (count == 10) { button.Content = string.Format($"DeadLock...."); } if (count == 11) { DeadLock(); } } }MainWindow.xaml<Window x:Class="WPFSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFSample" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition> </RowDefinition> <RowDefinition> </RowDefinition> </Grid.RowDefinitions> <ComboBox ItemsSource="{Binding Path=Data}" x:Name="comboBox" Grid.Row ="0" Grid.Column="0"/> <Button Content="0回" Grid.Row="1" Grid.Column="0" Click="Button_Click"/> </Grid> </Window>デッドロックに行く直前なら、プルダウンなどの操作できるが、一旦行ってしまうと何もできなくなる。
操作不能
回避方法
ConfigureAwait() を使うと、コンテキストのリストアをするか否かを制御できる。falseにすると、コンテキストをリストアして元のスレッドに戻ろうとしない(違うスレッドを使う)ので、デッドロックが起こらなくなる。
private async Task WaitAsAsync() { await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); }