- 投稿日:2021-01-07T15:02:48+09:00
AWS CloudShell に .NET 5 をインストールして、Windows向けの実行ファイルを作ってみる
はじめに
AWS CloudShellはAWSのマネジメントコンソール上で、Amazon Linux2を起動してAWSの各機能をコマンドラインベースで実行するための機能です。Amazon Linux2はCentOS7に近い環境なので、yumを使って.NET Coreをインストールすることができます。
今回はAWS CloudShell上に.NET 5をインストールして、アプリをビルド→実行→Windows向けの実行ファイルのダウンロードをしてみます。
# だれがうれしいんだ!?という内容ですが、、、CloudShellの起動と.NET Core5のインストール
AWS マネジメントコンソールにログインして
>_
なアイコンをクリックしてCloudShellを立ち上げます。
あとは、CentOS に .NET SDK または .NET ランタイムをインストールするの、CentOS7向けの手順を実行してセットアップしていきます。
まずはリポジトリにMicrosoftのリポジトリを追加してCloudShell$ sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm Retrieving https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm warning: waiting for transaction lock on /var/lib/rpm/.rpm.lock Preparing... ################################# [100%] Updating / installing... 1:packages-microsoft-prod-1.0-1 ################################# [100%]インストールを実行すると、実際にパッケージをダウンロードするタイミングと、インストールの前に確認を求められるので両方とも
y
で応答するればインストールは完了です。CloudShell$ sudo yum install dotnet-sdk-5.0 Loaded plugins: ovl, priorities packages-microsoft-com-prod | 3.0 kB 00:00:00 packages-microsoft-com-prod/primary_db | 278 kB 00:00:00 Resolving Dependencies --> Running transaction check ---> Package dotnet-sdk-5.0.x86_64 0:5.0.101-1 will be installed --> Processing Dependency: netstandard-targeting-pack-2.1 >= 2.1.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Processing Dependency: dotnet-runtime-5.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Processing Dependency: dotnet-targeting-pack-5.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Processing Dependency: dotnet-apphost-pack-5.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Processing Dependency: aspnetcore-runtime-5.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Processing Dependency: aspnetcore-targeting-pack-5.0 for package: dotnet-sdk-5.0-5.0.101-1.x86_64 --> Running transaction check ---> Package aspnetcore-runtime-5.0.x86_64 0:5.0.1-1 will be installed ---> Package aspnetcore-targeting-pack-5.0.x86_64 0:5.0.0-1 will be installed ---> Package dotnet-apphost-pack-5.0.x86_64 0:5.0.1-1 will be installed ---> Package dotnet-runtime-5.0.x86_64 0:5.0.1-1 will be installed --> Processing Dependency: dotnet-hostfxr-5.0 >= 5.0.1 for package: dotnet-runtime-5.0-5.0.1-1.x86_64 --> Processing Dependency: dotnet-runtime-deps-5.0 >= 5.0.1 for package: dotnet-runtime-5.0-5.0.1-1.x86_64 ---> Package dotnet-targeting-pack-5.0.x86_64 0:5.0.0-1 will be installed ---> Package netstandard-targeting-pack-2.1.x86_64 0:2.1.0-1 will be installed --> Running transaction check ---> Package dotnet-hostfxr-5.0.x86_64 0:5.0.1-1 will be installed --> Processing Dependency: dotnet-host >= 5.0.1 for package: dotnet-hostfxr-5.0-5.0.1-1.x86_64 ---> Package dotnet-runtime-deps-5.0.x86_64 0:5.0.1-1 will be installed --> Running transaction check ---> Package dotnet-host.x86_64 0:5.0.1-1 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================================= Package Arch Version Repository Size ================================================================================================= Installing: dotnet-sdk-5.0 x86_64 5.0.101-1 packages-microsoft-com-prod 80 M Installing for dependencies: aspnetcore-runtime-5.0 x86_64 5.0.1-1 packages-microsoft-com-prod 8.0 M aspnetcore-targeting-pack-5.0 x86_64 5.0.0-1 packages-microsoft-com-prod 2.1 M dotnet-apphost-pack-5.0 x86_64 5.0.1-1 packages-microsoft-com-prod 4.6 M dotnet-host x86_64 5.0.1-1 packages-microsoft-com-prod 64 k dotnet-hostfxr-5.0 x86_64 5.0.1-1 packages-microsoft-com-prod 170 k dotnet-runtime-5.0 x86_64 5.0.1-1 packages-microsoft-com-prod 29 M dotnet-runtime-deps-5.0 x86_64 5.0.1-1 packages-microsoft-com-prod 2.8 k dotnet-targeting-pack-5.0 x86_64 5.0.0-1 packages-microsoft-com-prod 3.1 M netstandard-targeting-pack-2.1 x86_64 2.1.0-1 packages-microsoft-com-prod 2.1 M Transaction Summary ================================================================================================= Install 1 Package (+9 Dependent packages) Total download size: 129 M Installed size: 369 M Is this ok [y/d/N]: y Downloading packages: warning: /var/cache/yum/x86_64/2/packages-microsoft-com-prod/packages/aspnetcore-targeting-pack-5.0.0.rpm: Header V4 RSA/SHA256 Signature, key ID be1229cf: NOKEY Public key for aspnetcore-targeting-pack-5.0.0.rpm is not installed (1/10): aspnetcore-targeting-pack-5.0.0.rpm | 2.1 MB 00:00:00 (2/10): dotnet-apphost-pack-5.0.1-x64.rpm | 4.6 MB 00:00:00 (3/10): dotnet-host-5.0.1-x64.rpm | 64 kB 00:00:00 (4/10): dotnet-hostfxr-5.0.1-x64.rpm | 170 kB 00:00:00 (5/10): aspnetcore-runtime-5.0.1-x64.rpm | 8.0 MB 00:00:01 (6/10): dotnet-runtime-deps-5.0.1-centos.7-x64.rpm | 2.8 kB 00:00:00 (7/10): dotnet-runtime-5.0.1-x64.rpm | 29 MB 00:00:01 (8/10): dotnet-targeting-pack-5.0.0-x64.rpm | 3.1 MB 00:00:00 (9/10): netstandard-targeting-pack-2.1.0-x64.rpm | 2.1 MB 00:00:00 (10/10): dotnet-sdk-5.0.101-x64.rpm | 80 MB 00:00:09 ------------------------------------------------------------------------------------------------- Total 11 MB/s | 129 MB 00:00:11 Retrieving key from https://packages.microsoft.com/keys/microsoft.asc Importing GPG key 0xBE1229CF: Userid : "Microsoft (Release signing) <gpgsecurity@microsoft.com>" Fingerprint: bc52 8686 b50d 79e3 39d3 721c eb3e 94ad be12 29cf From : https://packages.microsoft.com/keys/microsoft.asc Is this ok [y/N]: y Running transaction check Running transaction test Transaction test succeeded Running transaction Warning: RPMDB altered outside of yum. Installing : dotnet-targeting-pack-5.0-5.0.0-1.x86_64 1/10 Installing : aspnetcore-targeting-pack-5.0-5.0.0-1.x86_64 2/10 Installing : dotnet-host-5.0.1-1.x86_64 3/10 Installing : dotnet-hostfxr-5.0-5.0.1-1.x86_64 4/10 Installing : dotnet-runtime-deps-5.0-5.0.1-1.x86_64 5/10 Installing : dotnet-runtime-5.0-5.0.1-1.x86_64 6/10 Installing : aspnetcore-runtime-5.0-5.0.1-1.x86_64 7/10 Installing : netstandard-targeting-pack-2.1-2.1.0-1.x86_64 8/10 Installing : dotnet-apphost-pack-5.0-5.0.1-1.x86_64 9/10 Installing : dotnet-sdk-5.0-5.0.101-1.x86_64 10/10 This software may collect information about you and your use of the software, and send that to Microsoft. Please visit http://aka.ms/dotnet-cli-eula for more information. Welcome to .NET! --------------------- Learn more about .NET: https://aka.ms/dotnet-docs Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli-docs Telemetry --------- The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell. Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry Configuring... -------------- A command is running to populate your local package cache to improve restore speed and enable offline access. This command takes up to one minute to complete and only runs once. Verifying : dotnet-hostfxr-5.0-5.0.1-1.x86_64 1/10 Verifying : dotnet-apphost-pack-5.0-5.0.1-1.x86_64 2/10 Verifying : aspnetcore-targeting-pack-5.0-5.0.0-1.x86_64 3/10 Verifying : netstandard-targeting-pack-2.1-2.1.0-1.x86_64 4/10 Verifying : dotnet-runtime-5.0-5.0.1-1.x86_64 5/10 Verifying : aspnetcore-runtime-5.0-5.0.1-1.x86_64 6/10 Verifying : dotnet-targeting-pack-5.0-5.0.0-1.x86_64 7/10 Verifying : dotnet-sdk-5.0-5.0.101-1.x86_64 8/10 Verifying : dotnet-runtime-deps-5.0-5.0.1-1.x86_64 9/10 Verifying : dotnet-host-5.0.1-1.x86_64 10/10 Installed: dotnet-sdk-5.0.x86_64 0:5.0.101-1 Dependency Installed: aspnetcore-runtime-5.0.x86_64 0:5.0.1-1 aspnetcore-targeting-pack-5.0.x86_64 0:5.0.0-1 dotnet-apphost-pack-5.0.x86_64 0:5.0.1-1 dotnet-host.x86_64 0:5.0.1-1 dotnet-hostfxr-5.0.x86_64 0:5.0.1-1 dotnet-runtime-5.0.x86_64 0:5.0.1-1 dotnet-runtime-deps-5.0.x86_64 0:5.0.1-1 dotnet-targeting-pack-5.0.x86_64 0:5.0.0-1 netstandard-targeting-pack-2.1.x86_64 0:2.1.0-1 Complete!.NET 5がインストールされました。
CloudShell$ dotnet --version 5.0.101ビルドしてWindows向けの実行ファイルをダウンロードする
Console アプリを作成して実行してみる。
CloudShell$ mkdir consoleapp $ cd consoleapp $ dotnet new console $ dotnet run Hello World!Program.csを開き、
CloudShell$ ls bin obj Program.cs sample.csproj $ vi Program.csまぁ、よくある修正をして保存後、
Program.csusing System; namespace sample { class Program { static void Main(string[] args) { Console.WriteLine("Hello CloudShell"); } } }ビルドして実行すると反映されましたね。
CloudShell$ dotnet run Hello CloudShell
Windows 向けにビルドします。
CloudShell$ dotnet publish -r win-x86 -c Release -p:PublishSingleFile=true Microsoft (R) Build Engine version 16.8.0+126527ff1 for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. sample -> /home/cloudshell-user/sample/bin/Release/net5.0/win-x86/sample.dll sample -> /home/cloudshell-user/sample/bin/Release/net5.0/win-x86/publish/出来上がったファイルをtarで固めてダウンロードします。
CloudShell$ cd /home/cloudshell-user/sample/bin/Release/net5.0/win-x86/ $ tar cvfz ~/app.tar.gz publish/ publish/ publish/sample.exe publish/mscordaccore.dll publish/clrjit.dll publish/clrcompression.dll publish/sample.pdb publish/coreclr.dllActions → Download fileでファイルをダウンロードできます。
まとめ
- AWS CloudShell上でも入れれば.NET動くよ
- .NET のクロスコンパイラでWindows、Macの実行ファイルを作成できるよ
- ビルド方法によっては.NET ランタイムがインストールされていなくても動作するよ
CloudShellでは永続化されるのはHomeディレクトリ配下の1GBのファイルだけで、他は起動するたびに失われるので最初からインストールされているランタイムを使ったほうが良いですよね。PowerShell Coreも入っていますし。
- 投稿日:2021-01-07T11:01:00+09:00
【C#】Task
Task
単に「仕事」を意味する動作を指す。
同期型メソッドと非同期型メソッド
まず、同期型メソッドは1から順にTaskを実行するメソッド。
それに対して、非同期型は順序問わず、Taskを実行するメソッドを言う。★非同期型メソッドのキーワード
async修飾子
メソッド内でawait演算子を利用するための修飾子。sample.csprivate async void Click_Action(object sender, RoutedEventArgs e) { // 処理を記述 }await演算子
async修飾子が付いているメソッド内で1以上記述できる。
逆を言えばasync修飾子内に1つ以上のawait演算子が必要となる。Sample.csprivate async void Click_Action(object sender, RoutedEventArgs e) { this.botton.IsEnabled = flase; await MouseActionMethod(); // 何かしらの処理を呼び出し this.botton.IsEnabled = true; }
- 投稿日:2021-01-07T09:46:13+09:00
ASP.NET Core+NUnitでインテグレーションテスト
はじめに
ASP.NET CoreではコントローラーやRazorPagesに対するインテグレーションテストを想定して、インメモリでテスト用Webサーバーをホストするための
WebApplicationFactory
クラスがあらかじめ用意されています。このクラスの利用方法はASP.NET Core MVC アプリのテストにまとめられていますが、対象がXUnitになっているためNUnitの場合このままでは利用できません。
この記事では、ASP.NET Core+NUnitを利用したインテグレーションテストの開始方法を説明します。テスト用プロジェクトの作成と必要なパッケージのインストール
dotnetコマンドで作られるテスト対象のWebApiプロジェクトと、NUnitを利用したテストプロジェクトを追加していきます。
テストプロジェクトはテスト対象のWebApiプロジェクトに対する参照と、WebApplicationFactory
クラスを利用するためにMicrosoft.AspNetCore.Mvc.Testing
パッケージを追加します。dotnet new webapi -o ASPNETCoreNUnitItSample/WebApi dotnet new nunit -o ASPNETCoreNUnitItSample/WebApi.Tests cd ASPNETCoreNUnitItSample dotnet new sln dotnet sln add ./WebApi/WebApi.csproj dotnet sln add ./WebApi.Tests/WebApi.Tests.csproj dotnet add ./WebApi.Tests/WebApi.Tests.csproj reference --interactive ./WebApi/WebApi.csproj dotnet add ./WebApi.Tests/WebApi.Tests.csproj package Microsoft.AspNetCore.Mvc.TestingVisual Studioでソリューションを開くと、次のような構成のソリューションが作成されます。
インテグレーション用テスト用Webサーバーの起動コードを追加
まずはプロジェクトファイルの
<Project Sdk="Microsoft.NET.Sdk">
を<Project Sdk="Microsoft.NET.Sdk.Web">
に変更します。WebApiTests.csproj<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.1" /> <PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.16.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\WebApi\WebApi.csproj" /> </ItemGroup> </Project>続いて、テスト用Webサーバーの起動クラスを追加します。
ApiWebApplicationFactory.csusing Microsoft.AspNetCore.Mvc.Testing; namespace WebApi.Tests { public class ApiWebApplicationFactory: WebApplicationFactory<WebApi.Startup> { } }テストコードの追加
XUnitの例では、
IClassFixture<T>
を継承することでWebApplicationFactory
のインスタンスを取得していましたが、NUnitではIClassFixture
は使えないので、OneTimeSetup属性で指定したメソッドでApiWebApplicationFactory
を作成し、HttpClientを取り出してこれを利用します。WeatherForecastControllerTest.csusing System.Net.Http; using System.Threading.Tasks; using NuGet.Frameworks; using NUnit.Framework; namespace WebApi.Tests.Controllers { [TestFixture] class WeatherForecastControllerTest { private HttpClient _httpClient; [OneTimeSetUp] public void OneTimeSetup() { var factory = new ApiWebApplicationFactory(); _httpClient = factory.CreateClient(); } [Test] public async Task GetTest() { var response = await _httpClient.GetAsync("/WeatherForecast"); Assert.IsTrue(response.IsSuccessStatusCode); } } }まとめ
- NUnitでインテグレーションテストを実施する場合は、自分で
Microsoft.AspNetCore.Mvc.Testing
パッケージを追加する- Factoryの生成はOneTimeSetupで指定したメソッドで実施する
実際にインテグレーションテストを実施する場合は、ConfigureWebHostメソッドをオーバーライドしてデータベースの参照先をRDBからメモリーデータベースにしたり、外部サービスをモックに差し替えるなどいくつか追加で実施する必要がありますが、実施方法はXUnitと変わりがありませんのでASP.NET Core MVC アプリのテストのASP.NET Core アプリを機能テストするを参照してください。
- 投稿日:2021-01-07T05:33:39+09:00
C#でAtCoderデビューのための準備
まずは門を叩いて入門、
処理を高速化することやエラー処理のことはここでは考えないでおきます。標準入力
// 文字列の入力 string s = Console.ReadLine(); // 整数の入力 long n = long.Parse(Console.ReadLine()); // 文字列配列の入力 string[] inputStrArray = Console.ReadLine().Split(' '); // 整数配列の入力 long[] inputLongArray = Console.ReadLine().Split(' ').Select(i => long.Parse(i)).ToArray();配列の初期化
// 配列を生成する var array = new int[] {0, 2, 4, 6}; // [0,1...] の配列を生成する var array = Enumerable.Range(0, 5).ToArray(); // {0, 1, 2, 3, 4} // 初期値が全て同じ配列を生成する var array = Enumerable.Repeat(-1, 5).ToArray(); // { -1, -1, -1, -1, -1}参考
競技プログラミングのための C# (4.0 以降) の Tips 詰め合わせ
https://emkcsharp.hatenablog.com/entry/2013/Advent初心者がC#でAtCoderデビューするためのVSProjectテンプレート
https://qiita.com/sekikatsu/items/93c41c6c937ed1dfcf23AtCoderで使えそうなC# 7.0~8.0の新機能
https://www.terry-u16.net/entry/csharp-7-8-new-features-for-atcoder
- 投稿日:2021-01-07T05:21:26+09:00
有理数型を実装する
C#で有理数型を実装してみた例です。
有理数で実現したいこと
有理数型として下記の機能を作りたいと思います。
- 加算
- 減算
- 乗算
- 除算
- 比較
- 浮動小数点数への変換
型の定義
long
の既約分数として有理数を表現することにします。符号はどちらでも良いですが、分子に集約することにします。既約分数は分子と分母を最大公約数で割ると良いので、最大公約数をユークリッドの互除法で求めてしまえば良いです。
Fraction.cs/// <summary>有理数を既約分数で表す</summary> public readonly struct Fraction : IEquatable<Fraction>, IComparable<Fraction> { /// <summary>分子</summary> public long Numerator { get; } /// <summary>分母</summary> public long Denominator { get; } public Fraction(long 分子, long 分母) { var negative = (分子 ^ 分母) < 0; 分子 = Math.Abs(分子); 分母 = Math.Abs(分母); var gcd = Gcd(分母, 分子); _numerator = 分子 / gcd; if (negative) _numerator = -_numerator; _denominator = 分母 / gcd - 1; } /// <summary> /// 最大公約数をユークリッドの互除法で求める /// </summary> public static long Gcd(long a, long b) => b > a ? Gcd(b, a) : (b == 0 ? a : Gcd(b, a % b)); }四則演算の定義
小学校の算数で習う分数の計算をそのまま実装します。
C# は演算子オーバーロードを定義できるので、素直に実装します。加算、減算した結果の分母は計算するそれぞれの分母の最小公倍数になります。
x, y の最小公倍数は $ \frac{x y}{最大公約数} $ と表せます。Fraction.cspublic static Fraction operator -(Fraction x) => new Fraction(-x.Numerator, x.Denominator); public static Fraction operator +(Fraction x, Fraction y) { var gcd = Gcd(x.Denominator, y.Denominator); var lcm = x.Denominator / gcd * y.Denominator; return new Fraction((x.Numerator * y.Denominator + y.Numerator * x.Denominator) / gcd, lcm); } public static Fraction operator -(Fraction x, Fraction y) { var gcd = Gcd(x.Denominator, y.Denominator); var lcm = x.Denominator / gcd * y.Denominator; return new Fraction((x.Numerator * y.Denominator - y.Numerator * x.Denominator) / gcd, lcm); } public static Fraction operator *(Fraction x, Fraction y) => new Fraction(x.Numerator * y.Numerator, x.Denominator * y.Denominator); public static Fraction operator /(Fraction x, Fraction y) => new Fraction(x.Numerator * y.Denominator, x.Denominator * y.Numerator);比較の定義
\frac{a}{b} < \frac{c}{d}という不等式は両辺に $ bd $ を掛けて、
ad < bdと変形できるので、
long
型の演算で定義できます。Fraction.cspublic int CompareTo(Fraction other) => (this.Numerator * other.Denominator).CompareTo(other.Numerator * this.Denominator); public static bool operator ==(Fraction x, Fraction y) => x.Equals(y); public static bool operator !=(Fraction x, Fraction y) => !x.Equals(y); public static bool operator >=(Fraction x, Fraction y) => x.CompareTo(y) >= 0; public static bool operator <=(Fraction x, Fraction y) => x.CompareTo(y) <= 0; public static bool operator >(Fraction x, Fraction y) => x.CompareTo(y) > 0; public static bool operator <(Fraction x, Fraction y) => x.CompareTo(y) < 0;変換
double
への変換やlong
からの暗黙的な変換も定義しておくと使いやすいでしょう。
long
からの変換は分母を1にするだけです。Fraction.cspublic double ToDouble() => (double)Numerator / Denominator; public static implicit operator Fraction(long x) => new Fraction(x, 1);デフォルト値の問題
しかし、このような実装ではデフォルト値が
0/0
となってしまいます。そのため
new Fraction(2,3) * default(Fraction)
がゼロ除算を引き起こしてしまいます。
new Fraction(2,3) + default(Fraction) == new new Fraction(2,3)
new Fraction(2,3) + default(Fraction) == new new Fraction(0,1)
を満たすようにしたいところです。
これは、型の内部表現では分母の値を-1すると解決します。
つまり、分母が1のときは内部では0を保持、分母が2のときは内部では1を保持、という具合です。こうすることで、
default(Fraction) == new new Fraction(0,1)
となり上記のデフォルト値での演算も期待通りになります。Fraction.cspublic readonly struct Fraction { /// <summary>分子</summary> private readonly long _numerator; /// <summary>分子</summary> public long Numerator => _numerator; /// <summary>分母 - 1 (default を 0/0 ではなく 0/1 にしたい)</summary> private readonly long _denominator; /// <summary>分母</summary> public long Denominator => _denominator + 1; public Fraction(long 分子, long 分母) { var negative = (分子 ^ 分母) < 0; 分子 = Math.Abs(分子); 分母 = Math.Abs(分母); if (分子 == 0) { _numerator = 0; _denominator = 0; } else { var gcd = Gcd(分母, 分子); _numerator = 分子 / gcd; if (negative) _numerator = -_numerator; _denominator = 分母 / gcd - 1; } } }完成
上記の検討から有理数型を作ることができました。
Fraction.cs/// <summary>有理数を既約分数で表す</summary> public readonly struct Fraction : IEquatable<Fraction>, IComparable<Fraction> { /// <summary>分子</summary> private readonly long _numerator; /// <summary>分子</summary> public long Numerator => _numerator; /// <summary>分母 - 1 (default を 0/0 ではなく 0/1 にしたい)</summary> private readonly long _denominator; /// <summary>分母</summary> public long Denominator => _denominator + 1; public Fraction(long 分子, long 分母) { var negative = (分子 ^ 分母) < 0; 分子 = Math.Abs(分子); 分母 = Math.Abs(分母); if (分子 == 0) { _numerator = 0; _denominator = 0; } else { var gcd = Gcd(分母, 分子); _numerator = 分子 / gcd; if (negative) _numerator = -_numerator; _denominator = 分母 / gcd - 1; } } /// <summary> /// 最大公約数をユークリッドの互除法で求める /// </summary> public static long Gcd(long a, long b) => b > a ? Gcd(b, a) : (b == 0 ? a : Gcd(b, a % b)); public override string ToString() => $"{Numerator}/{Denominator}"; public override bool Equals(object obj) => obj is Fraction f && Equals(f); public bool Equals(Fraction other) => this._numerator == other._numerator && this._denominator == other._denominator; public override int GetHashCode() => HashCode.Combine(_numerator, _denominator); public static implicit operator Fraction(long x) => new Fraction(x, 1); public static Fraction operator -(Fraction x) => new Fraction(-x.Numerator, x.Denominator); public static Fraction operator +(Fraction x, Fraction y) { var gcd = Gcd(x.Denominator, y.Denominator); var lcm = x.Denominator / gcd * y.Denominator; return new Fraction((x.Numerator * y.Denominator + y.Numerator * x.Denominator) / gcd, lcm); } public static Fraction operator -(Fraction x, Fraction y) { var gcd = Gcd(x.Denominator, y.Denominator); var lcm = x.Denominator / gcd * y.Denominator; return new Fraction((x.Numerator * y.Denominator - y.Numerator * x.Denominator) / gcd, lcm); } public static Fraction operator *(Fraction x, Fraction y) => new Fraction(x.Numerator * y.Numerator, x.Denominator * y.Denominator); public static Fraction operator /(Fraction x, Fraction y) => new Fraction(x.Numerator * y.Denominator, x.Denominator * y.Numerator); public int CompareTo(Fraction other) => (this.Numerator * other.Denominator).CompareTo(other.Numerator * this.Denominator); public static bool operator ==(Fraction x, Fraction y) => x.Equals(y); public static bool operator !=(Fraction x, Fraction y) => !x.Equals(y); public static bool operator >=(Fraction x, Fraction y) => x.CompareTo(y) >= 0; public static bool operator <=(Fraction x, Fraction y) => x.CompareTo(y) <= 0; public static bool operator >(Fraction x, Fraction y) => x.CompareTo(y) > 0; public static bool operator <(Fraction x, Fraction y) => x.CompareTo(y) < 0; public Fraction Inverse() => new Fraction(Denominator, Numerator); public double ToDouble() => (double)Numerator / Denominator; }