csbindgenを使ってみる

# csbindgenってなに
[nunecc](https://github.com/neuecc)さんが開発したライブラリで、**C/C++ -> C#, Rust -> C#のFFI補助ツール**となっています。

https://github.com/Cysharp/csbindgen

:::note info
FFI(Foreign Function Interface)とは  
簡単にいうと別言語の関数を呼び出す機能で、たいてい既存のライブラリ（特にC/C++）を別言語でも使いたい時に用いられています。
:::

# csbindgenのここがすごい
ここからはcsbinfgenの利点を書いていきます。
## `[DLLImport]`の自動化
今まではFFIで使用したい関数**すべて**につけないといけなかったのが、csbindgenを用いることで自動生成されるようになります。  
このコード×数十とかいう日には苦痛でしかなかったのでありがたすぎます。  
```csharp
[DllImport("foge", EntryPoint = "foge_fuga",
 CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern int fuga(int a);
```

## オブジェクト指向的にFFI元関数を呼び出せる
FFIの都合上関数のみ呼び出せるので`foge.fuga()`ではなく`NativeMethods.fuga(hoge)`のようになってしまいます。  
ここで`GroupedNativeMethods`を用いると、ソースコードジェネレータで`foge.fuga()`用の`fuga()`を自動生成してくれます。  

## RustとCの両方からFFIができる
`csbindgen`によってRust -> C#のFFIが簡単にできるようになりました。また、Cのコードは`bindgen`という別ライブラリでC->Rustを実現できます。  
これを組み合わせてCの処理をRustで整えてC#に渡すといったことが簡単にできます。Cのコードベースが膨大でRust/C#に完全には移せないときにはかなり有効となります。

## コンパイルが簡単
これはcsbindgenの機能ではないのですが、C/C++のコンパイルをRustの`cc`/`cmake`クレートを用いることで、楽にC/C++/Rust統合ネイティブライブラリを作成できるので、ネイティブライブラリ生成も`cargo`のビルドシステムに乗せることでさらに簡略化できます。


# さわってみる
## 環境の準備
以下のサイトから、csbindgenとbindgenに必要なツールをインストールしてください。
- rustのインストール
  - https://www.rust-lang.org/ja/learn/get-started
- NET 8のインストール
  - https://dotnet.microsoft.com/ja-jp/download/dotnet/8.0
- LLVM(clang)のインストール
  - https://rust-lang.github.io/rust-bindgen/requirements.html

また、今回の説明ではvscodeを用いているのでそちらの方でやるとよいです。
- vscodeのインストール
  - https://code.visualstudio.com/download

## プロジェクトの準備
今回の勉強会用リポジトリを立てたのでそちらからクローンしてきてください。  
```bash
git clone https://github.com/aiueo-1234/csbindgen-handson.git
# sshがいい人は git clone git@github.com:aiueo-1234/csbindgen-handson.git
```

:::note info
以下のリンクに今回使用したコードがあります。
手元で確認はいいやって方はこちらを参照してください
https://github.com/aiueo-1234/csbindgen-handson/tree/kc3
:::

クローンしてvscodeで開いたら以下のような感じになっていると思います。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476116/b4670af2-5ac9-d402-2748-ce276d59ab3b.png)


## C#セットアップ
ソリューションファイルとFFI用ライブラリ・実行用コンソールアプリを作成します。  
以下のコマンドをvscodeのターミナルで実行すると作成できます。
```bash
# wsl用
dotnet new sln -n CsbindgenHandsOn
dotnet new classlib -n CsbindgenHandsOn -o src/CsbindgenHandsOn
dotnet new console -n ConsoleApp -o sandbox/ConsoleApp
dotnet sln add src/CsbindgenHandsOn/CsbindgenHandsOn.csproj
dotnet sln add sandbox/ConsoleApp/ConsoleApp.csproj 
```
なお、以降Windows本体で行っている人は適宜`/`（スラッシュ）を`\`(バックスラッシュ)か`¥`に読み替えてください。

### C#の依存関係設定
コンソールアプリにFFIライブラリへの参照を追加します。  
`ConsoleApp.csproj`を開いて以下の内容を`<PropertyGroup>`と同一階層に追記してください。  
```xml:ConsoleApp.csproj
<Project Sdk="Microsoft.NET.Sdk">

　<PropertyGroup>
　  <!-- 省略 -->
　</PropertyGroup>
　　
　<ItemGroup>
　  <ProjectReference Include="..\..\src\CsbindgenHandsOn\CsbindgenHandsOn.csproj" />
　</ItemGroup>

</Project>
```

### csbindgenの参照追加
コンソールアプリにFFIライブラリへの参照を追加します。
`CsbindgenHandsOn.csproj`を開いて以下の内容を`<PropertyGroup>`と同一階層に追記してください。
```xml:CsbindgenHandsOn.csproj
<ItemGroup>
  <PackageReference Include="csbindgen" Version="1.9.3">
    <IncludeAssets>
      runtime; build; native; contentfiles; analyzers; buildtransitive
    </IncludeAssets>
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>
```

### C#ビルド設定
`CsbindgenHandsOn`プロジェクトに以下の変更を加えます。
- unsafeコードを許可
- rustライブラリをC#のバイナリ側にコピーする設定
- rustのコンパイルをしてC#のプロジェクトにコピーする設定

`CsbindgenHandsOn.csproj`を開いて以下のコードと追記してください。
```xml:CsbindgenHandsOn.csproj
<PropertyGroup>
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <!-- ここ -->
</PropertyGroup>

<ItemGroup>
  <None Include="runtimes\$(RuntimeIdentifire)\native\csbindgenhandson.dll"
        Condition="$(RuntimeIdentifire.StartsWith(win))">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </None>
  <None Include="runtimes\$(RuntimeIdentifire)\native\libcsbindgenhandson.so"
        Condition="$(RuntimeIdentifire.StartsWith(linux))">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </None>
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Configuration)'=='Debug'">
  <Exec Command="cargo build"
        WorkingDirectory="$([System.IO.Path]::Combine($(ProjectDir),../libcsbindgenhandson))" />
  <Copy Condition="$(RuntimeIdentifire.StartsWith(win))" 
        SourceFiles="$([System.IO.Path]::Combine($(ProjectDir),../libcsbindgenhandson/target/debug/csbindgenhandson.dll))" 
        DestinationFolder="$([System.IO.Path]::Combine($(ProjectDir),../CsbindgenHandsOn,runtimes\$(RuntimeIdentifire)\native))" />
  <Copy Condition="$(RuntimeIdentifire.StartsWith(linux))"
        SourceFiles="$([System.IO.Path]::Combine($(ProjectDir),../libcsbindgenhandson/target/debug/libcsbindgenhandson.so))" 
        DestinationFolder="$([System.IO.Path]::Combine($(ProjectDir),../CsbindgenHandsOn,runtimes\$(RuntimeIdentifire)\native))" />
</Target>
```

### RuntimeIdentifireの決定
RIDを決めるため、`Directory.Build.props`を`sln`ファイルと同階層に作成してください。ここはlinux/wslの人と、windowsの人で異なるので注意が必要です。

WSLの人
```xml:Directory.Build.props
<Project>
  <PropertyGroup>
    <RuntimeIdentifire>linux-x64</RuntimeIdentifire>
  </PropertyGroup>
</Project>
```
Windowsの人
```xml:Directory.Build.props
<Project>
  <PropertyGroup>
    <RuntimeIdentifire>win-x64</RuntimeIdentifire>
  </PropertyGroup>
</Project>
```
  

## Rustセットアップ
FFI用のRustライブラリを作成します。vscodeで新しいターミナルを開いて以下のコマンドを実行してください。  
```bash
cd ./src
cargo new --lib ./libcsbindgenhandson
```


### Rustパッケージ設定
`Cargo.toml`に以下の内容を追記してください。  
```toml:Cargo.toml
[lib]
crate-type = ["cdylib"]
name = "csbindgenhandson"

[build-dependencies]
csbindgen = "1.9.3"
cc = "1.1"
bindgen = "0.70"
```

### rust-analyzer設定
`.vscode/settings.json`を作成し以下の内容を追記してください。ここまで出来たらvscodeをリロードしましょう。
```json:.vscode/settings.json
{
    "rust-analyzer.linkedProjects": [
        "src/libcsbindgenhandson/Cargo.toml"
    ],
}
```


# Rustの関数を呼び出してみよう
ここからは実際にRustの関数を呼び出してみましょう。まず以下のように`lib.rs`を書き換えます。  
```rust:lib.rs
#[no_mangle]
pub extern "C" fn rust_add(x: i32, y: i32) -> i32 {
    x + y
}
```

次に、`Cargo.toml`と同じ階層に`build.rs`を作成して以下のコードを書き込みましょう。しばらくすると`CsbindgenHandsOn/Native/NativeMethods.g.cs`が作成されるはずです。
```rust:build.rs
fn main(){
    csbindgen::Builder::default()
        .input_extern_file("src/lib.rs")
        .csharp_dll_name("csbindgenhandson")
        .csharp_namespace("CsbindgenHandsOn.Native")
        .generate_csharp_file("../CsbindgenHandsOn/Native/NativeMethods.g.cs")
        .unwrap();
}
```

また、`CsbindgenHandsOn`に`NativeMethods.DllImportResolver.cs`を作成して以下のコードをかきこみます。
```cs:CsbindgenHandsOn/NativeMethods.DllImportResolver.cs
using System.Reflection;
using System.Runtime.InteropServices;

namespace CsbindgenHandsOn.Native
{
    internal static unsafe partial class NativeMethods
    {
        static NativeMethods()
        {
            NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, DllImportResolver);
        }

        static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
        {
            if (libraryName == __DllName)
            {
                var path = "runtimes/";
                var extension = "";
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    path += "win-";
                    extension = ".dll";
                }
                else
                {
                    path += "linux-";
                    extension = ".so";
                }
                
                if (RuntimeInformation.OSArchitecture == Architecture.X64)
                {
                    path += "x64";
                }
                else if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
                {
                    path += "arm64";
                }
                path += $"/native/{(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)?"lib":"")}{__DllName}{extension}";

                return NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, path), assembly, searchPath);
            }
            return IntPtr.Zero;
        }
    }
}
```

さらに、`CsbindgenHandsOn/Class1.cs`のコードを以下のコードに書き換えて、ファイルを`BasicFunctionCall.cs`にしましょう。
```cs:CsbindgenHandsOn/BasicFunctionCall.cs
namespace CsbindgenHandsOn;

public class BasicFunctionCall
{
    public void CallRustAdd(int a, int b){
        Console.WriteLine($"rust_add({a}, {b}): {Native.NativeMethods.rust_add(a,b)}");
    }
}
```

最後に、`ConsoleApp/Program.cs`のコードを以下のコードに書き換えて実行してみましょう！ 　
```cs:ConsoleApp/Program.cs
using CsbindgenHandsOn;

var c = new BasicFunctionCall();
c.CallRustAdd(1,1);
```
ここまでできていれば。実行すると`rust_add(1, 1): 2`と表示されるはずです。


## Rust -> C#時のcsbindgenの使い方
`build.rs`で`csbindgen::Builder::default()`に対して以下の関数をチェーンすれば使うことができます。
- input_extern_file("rust_file_path")
  - FFI元の関数が書かれたファイルのパスを渡します
- csharp_dll_name("dll_name")
  - `[DllImport]`の`dllName`。つまりRustのライブラリ名です
    - 今回は`Cargp.toml`の`[lib]`の`name`を設定したのでそれと同じにします
- csharp_namespace("csharp_namespace")
  - 生成したFFI用C#クラスの名前空間を設定します
- generate_csharp_file("csharp_file_path")
  - 生成したFFI用C#クラスの出力先を指定します
  - これを一番最後に呼びましょう


## Rust側のコード解説
初めて見るキーワードが出てきた人もいるかもしれないので、ちょっと排泄しておきます。
- #[no_mangle]
  - Rustコンパイラは、シンボル名をネイティブコードリンカが期待するものとは異なるものにマングル（難読化）するのでそれを防ぎます
- pub extern "C"
  - ABI(Application Binary Interface)をCコンパイラがサポートするものにします
  - C#のFFIもこの形式をサポートしているのでこれを設定しましょう


## C#側のコード解説
- DllImportResolver
  - 結びつけるネイティブライブラリを指定するために用います
  - windowsとlinux,WSLではライブラリの拡張子やプレフィックスの有無などの違いがあるのでこれを用いて調整しましょう
    - Windows
      - 拡張子：.dll
      - プレフィックス：なし
    - linux, WSL
      - 拡張子：.so
      - プレフィックス：lib


# Cの関数を呼び出してみよう
それでは、Cの関数を呼び出してみましょう。  
## C/Rust側の準備
始めに、`libcsbindgenhandson/src`に`c`ディレクトリを作り、`myMath.h`を作成し以下のコードを書き込みます。  
```c:libcsbindgenhandson/src/c/myMath.h
#ifndef MYMATH_H_
#define MYMATH_H_

int myMath_mul(int a, int b);
int myMath_add(int a, int b);

#endif
```

次に、`libcsbindgenhandson/src/c`ディレクトリで、`myMath.c`を作成し以下のコードを書き込みます。
```c:libcsbindgenhandson/src/c/myMath.c
#include "myMath.h"

int myMath_mul(int a, int b){
    return a * b;
}

int myMath_add(int a, int b){
    return a + b;
}
```

さらに、以下のコードを`build.rs`の`main`関数に書き込みましょう。
こうすることで、C -> RustのFFIコード生成(`bindgen`)とCのコンパイル(`cc`)をしてくれます。
```rust:build.rs
bindgen::Builder::default()
       .header("src/c/myMath.h").generate().unwrap()
       .write_to_file("src/myMath.rs").unwrap();
cc::Build::new()
    .file("src/c/myMath.c")
    .try_compile("myMath").unwrap();
csbindgen::Builder::default()
    .input_bindgen_file("src/myMath.rs")
    .rust_method_prefix("cffi_")
    .rust_file_header("use super::myMath::*;")
    .csharp_entry_point_prefix("cffi_")
    .csharp_dll_name("csbindgenhandson")
    .csharp_namespace("CsbindgenHandsOn.Native")
    .csharp_class_name("CNativeMethodsMyMath")
    .generate_to_file(
        "src/myMath_ffi.rs",
        "../CsbindgenHandsOn/Native/CNativeMethodsMyMath.g.cs",
    )
    .unwrap();
```

また、`lib.rs`の先頭に下記コードを追記します。
```rust:lib.rs
#[allow(non_snake_case)]
mod myMath;

#[allow(non_snake_case)]
mod myMath_ffi;
```
これをしないと生成された`myMath`と`myMath_ffi`をrustコンパイラが認識しないため、なぜかコンパイルしても関数がないとか言われる事態になります。

## C#の準備
まず、`CsbindgenHandsOn/BasicFunctionCall.cs`のクラスに以下の関数を足します。
```cs:CsbindgenHandsOn/BasicFunctionCall.cs
public void CallMyMathAdd(int a, int b){
    Console.WriteLine($"myMath_add({a}, {b}): {Native.CNativeMethodsMyMath.myMath_add(a,b)}");
}
```

最後に、`ConsoleApp/Program.cs`のコードに以下のコードを追加して実行しましょう！
```cs:ConsoleApp/Program.cs
c.CallMyMathAdd(2,2);
```

ここまでできていれば、実行すると前の章の結果に加えて`myMath_add(2, 2): 4`と表示されるはずです


## C -> C#時のcsbindgenの使い方
まず、使いたいCファイルのヘッダファイルを`bindgen`に読み込ませてC->RustのFFIコードを生成させます。  
さらに、使いたいCファイルを`cc`を用いてコンパイルしましょう。この時`cc`がRustライブラリにCのコンパイル結果をバインドしてくれます。  
`bindgen`, `cc`の詳しい使い方は本題ではないので省きますが、複雑なプログラムであればこれらのクレートを駆使することになります。  
ここまでできたら、`build.rs`で`csbindgen::Builder::default()`に対して以下の関数をチェーンします。
- input_bindgen_file("bindgen_file_path")
  - `bindgen`が生成したファイルへのパス
- rust_file_header("use super::myMath::*;")
  - `bindgen`が生成したファイルを、`csbindgen`によって生成されたコードが読み込むための`use`を書く
- rust_method_prefix("prefix_")
- csharp_entry_point_prefix("prefix_")
  - RustとC#でのFFI関数をリンクする際にCの関数と被らないようにするためのもの。
  - `rust_method_prefix`と`csharp_entry_point_prefix`は必ずそろえる
- csharp_dll_name("csbindgenhandson")
- csharp_namespace("CsbindgenHandsOn.Native")
- csharp_class_name("CNativeMethodsMyMath")
  - 上三つはRust -> C#のときと同じです。
- generate_to_file("rust_ffi_path", "csharp_ffi_path")
  - Rust, C#のFFIコードの出力先を指定します。


# C -> Rust -> C#で連携してみる
これまでの知識をもちいて、Cの処理をRustで包んでC#に渡してみましょう。  
具体的にはmyMath_mulをもちいてRustでpow関数を実装し、それをC#から呼べるようにしてみます。  
始めに、`lib.rs`に以下のコードを追記します。  
```rust:lib.rs
use ::std::os::raw::c_int;

#[no_mangle]
pub unsafe extern "C" fn rust_pow(x: c_int, y: c_int) -> c_int {
    let mut ret: c_int = 1;
    for _ in 1..=y {
        ret = myMath::myMath_mul(ret, x);
    }
    ret
}
```

次に、`CsbindgenHandsOn/BasicFunctionCall.cs`のクラスに以下の関数を足します。  
```cs:CsbindgenHandsOn/BasicFunctionCall.cs
public void CallRustPow(int a, int b){
    Console.WriteLine($"rust_pow({a}, {b}): {Native.NativeMethods.rust_pow(a,b)}");
}
```

最後に、`ConsoleApp/Program.cs`のコードに以下のコードを追加して実行しましょう！  
```cs:ConsoleApp/Program.cs
c.CallRustPow(2,3);
```

最終的な出力結果が以下のようになっているはずです。  
```
rust_add(1, 1): 2
myMath_add(2, 2): 4
rust_pow(2, 3): 8
```


# GroupedNativeMethodsを使ってみよう
csbindgen固有の機能となる、GroupedNativeMethodsを使ってみましょう。  


## GroupedNativeMethodsとは
オブジェクト指向的にFFI元関数を呼び出せるようにする機能です。  
これは、FFIの都合上`foge.fuga()`ではなく`NativeMethods.fuga(hoge)`のようになってしまうため、ソースコードジェネレータで`foge.fuga()`用の`fuga()`を自動生成してくれます。  
今回はC言語でかなり簡素化したスタックを扱って試してみましょう。  


## C/Rust側の準備
始めに、`libcsbindgenhandson/src/c`ディレクトリで、`myStack.h`を作成し以下のコードを書き込みます。
```c:libcsbindgenhandson/src/c/myStack.h
#ifndef MYSTACK_H_
#define MYSTACK_H_

typedef struct MyStack
{
    int index;
    int *data;
} MyStack;

MyStack *myStack_create(int maxLength);
int myStack_pop(MyStack *myStack);
void myStack_push(MyStack *myStack, int val);
void myStack_delete(MyStack *myStack);

#endif
```

次に、`libcsbindgenhandson/src/c`ディレクトリで、`myStack.c`を作成し以下のコードを書き込みます。  
```c:libcsbindgenhandson/src/c/myStack.c
#include "myStack.h"
#include <stdlib.h>

MyStack *myStack_create(int maxLength)
{
    MyStack *ret = malloc(sizeof(MyStack));
    if (ret == NULL)
    {
        return NULL;
    }
    int *data = malloc(sizeof(int) * maxLength);
    if (data == NULL)
    {
        free(ret);
        return NULL;
    }
    ret->index = -1;
    ret->data = data;
    return ret;
}

int myStack_pop(MyStack *myStack){
    return myStack->data[myStack->index--];
}

void myStack_push(MyStack *myStack, int val){
    myStack->data[++myStack->index]=val;
}

void myStack_delete(MyStack *myStack){
    free(myStack->data);
    free(myStack);
}
```

さらに、`build.rs`の`main`関数に下記コードを追記します。  
```rust:build.rs
bindgen::Builder::default()
    .header("src/c/myStack.h").generate().unwrap()
    .write_to_file("src/myStack.rs").unwrap();
cc::Build::new()
    .file("src/c/myStack.c").try_compile("myStack").unwrap();
csbindgen::Builder::default()
    .input_bindgen_file("src/myStack.rs")
    .rust_method_prefix("cffi_")
    .rust_file_header("use super::myStack::*;")
    .csharp_entry_point_prefix("cffi_")
    .csharp_dll_name("csbindgenhandson")
    .csharp_namespace("CsbindgenHandsOn.Native")
    .csharp_class_name("CNativeMethodsMyStack")
    .generate_to_file(
        "src/myStack_ffi.rs",
        "../CsbindgenHandsOn/Native/CNativeMethodsMyStack.g.cs",
    ).unwrap();
```

最後に、`lib.rs`に下のコードを追記して、Rustコンパイラに生成したコードを認識させます。
```rust:lib.rs
#[allow(non_snake_case)]
mod myStack;

#[allow(non_snake_case)]
mod myStack_ffi;
```

## C#の準備
始めに、`CsbindgenHandsOn/Native`ディレクトリに`CNativeMethodsMyStack.cs`ファイルを作成し下記コードを書き込みます。
```cs:CsbindgenHandsOn/Native/CNativeMethodsMyStack.cs
using GroupedNativeMethodsGenerator;

namespace CsbindgenHandsOn.Native
{
    [GroupedNativeMethods(removePrefix: "myStack")]
    internal static unsafe partial class CNativeMethodsMyStack { }
}
```

次に、`CsbindgenHandsOn`ディレクトリに`TestGroupedNativeMethods.cs`ファイルを作成し下記コードを書き込みます。
```cs:CsbindgenHandsOn/TestGroupedNativeMethods.cs
using CsbindgenHandsOn.Native;

namespace CsbindgenHandsOn
{
    public sealed unsafe class TestGroupedNativeMethods 
                               : IDisposable
    {
        private bool _disposed;
        private readonly MyStack* _stack;
        public TestGroupedNativeMethods()
        {
            _stack = CNativeMethodsMyStack.myStack_create(5);
        }
        public void PushAndPop(ReadOnlySpan<int> numbers)
        {
            for (int i = 0; i < numbers.Length && i < 5; i++)
            {
                _stack->Push(numbers[i]);
                Console.WriteLine($"pushed {numbers[i]}");
            }
            for (int i = 0;i < numbers.Length && i != 5; i++)
            {
                Console.WriteLine($"popped {_stack->Pop()}");
            }
        }
        public void Dispose()
        {
            if (_disposed) return;
            _stack->Delete();
            _disposed = true;
        }
    }
}
```

最後に、`ConsoleApp/Program.cs`のコードに以下のコードを追加して実行しましょう！
```cs:ConsoleApp/Program.cs
using var t = new TestGroupedNativeMethods();
t.PushAndPop([1,2,3,4,5]);
```

このコードの出力結果が以下のようになっているはずです。
```
pushed 1
pushed 2
pushed 3
pushed 4
pushed 5
popped 5
popped 4
popped 3
popped 2
popped 1
```

## GroupedNativeMethodsの使い方 
`[GroupedNativeMethods]`属性を`csbindgen`で生成されたC#FFI用クラスに付けます。  
この際、生成されたコードに対して属性をつけるのではなく、上書きされないように、`partial`クラスで別ファイルにしてつけましょう。  
`[GroupedNativeMethods]`属性をつけたクラスに対してソースコードジェネレータがソースコードを生成してくれるので、`bindgen`側でこの機能を使用したいメソッドをまとめるorヘッダファイルで分けてRust用コードを生成するのが楽です。   

# 最後に
ここまでお付き合いくださりありがとうございました。  
ここが違うとかがあればコメントで指摘していただけると幸いです。  
それではよいC#ライフを！