[C#]プリプロセッサと.csprojでクロスプラットフォームにする

# 課題
今までファイル選択やらちょっとしたダイアログにWindows Formsを使っていましたが、それだと今持っているMacBookで実行できないことに気づきました。それだと外で気軽に編集できないし、何よりWindowsだけに向けたアプリになってしまうのでそのアプリを使ってくれる人が少しだけ減ってしまうことになります。
# 解決策と問題点
自分はファイル選択や、ちょっとメッセージを表示するくらいにしかWindows Formsを使っていなかったので、普通にコンソールで代用できるかなと思いました。~~まぁ頑張ればそのOS用のUIを使えたかもしれませんが~~
そこで、`#if`を使ってWindowsの時はWindows Formsを使って、それ以外はコンソールにするみたいな感じにしようと思いましたが、`#if`で使う用の`_WINDOWS_`みたいにOSごとに定義されるやつがなかったんです。つまり、コンパイル時にOSごとにコードを分けることができなくて、Windows Formsが使われてるコードをコンパイルするときにエラーが出るんです。
ちょっと調べてみると、.csprojの`<PropertyGroup Condition="XXX">`のように、`XXX`のところに条件を入れてOSごとに定義できることが分かりました。

https://stackoverflow.com/questions/30153797

https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions#systemoperatingsystem-property-functions

```Project.csproj
...
  <PropertyGroup Condition="$([System.OperatingSystem]::IsWindows())">
    <DefineConstants>_WINDOWS_</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition="$([System.OperatingSystem]::IsMacOS())">
    <DefineConstants>_MACOS_</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition="$([System.OperatingSystem]::IsLinux())">
    <DefineConstants>_LINUX_</DefineConstants>
  </PropertyGroup>
...
```
こうすることで、Windowsなら`_WINDOWS_`、MacOSなら`_MACOS_`のように、OSごとに定義することができます。
やっとできたのでビルドしてみると、.csprojでWindows Formが有効になってるとかターゲットフレームワークが`.net6.0-windows`になってるとかで(正確なエラーは覚えていませんが)エラーが出ました。
そこで.csprojの上の方を見てみると、`<UseWindowsForms>true</UseWindowsForms>`とかいういかにもWindows Formsを使うって宣言してる行と、`<TargetFramework>net6.0-windows</TargetFramework>`がありました。
```Project.csproj
...
  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>
...
```
OSごとにフレームワークを変えるってできるのか？って思いましたが、そういえばさっき`<PropertyGroup>`を使っていました。
解決策として、普通にさっきのOSごとの`<PropertyGroup>`に移しました。
```diff_xml:Project.csproj
...
   <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
-    <TargetFramework>net6.0-windows</TargetFramework>
-    <UseWindowsForms>true</UseWindowsForms>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsWindows())">
     <DefineConstants>_WINDOWS_</DefineConstants>
+    <TargetFramework>net6.0-windows</TargetFramework>
+    <UseWindowsForms>true</UseWindowsForms>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsMacOS())">
     <DefineConstants>_MACOS_</DefineConstants>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsLinux())">
     <DefineConstants>_LINUX_</DefineConstants>
   </PropertyGroup>
...
```
# まとめ
.csprojをこういう風に変更する
```diff_xml:Project.csproj
...
   <PropertyGroup>
+    <TargetFramework>netX.X</TargetFramework>
-    <TargetFramework>netX.X-windows</TargetFramework>
-    <UseWindowsForms>true</UseWindowsForms>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsWindows())">
     <DefineConstants>_WINDOWS_</DefineConstants>
+    <TargetFramework>netX.X-windows</TargetFramework>
+    <UseWindowsForms>true</UseWindowsForms>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsMacOS())">
     <DefineConstants>_MACOS_</DefineConstants>
   </PropertyGroup>
 
   <PropertyGroup Condition="$([System.OperatingSystem]::IsLinux())">
     <DefineConstants>_LINUX_</DefineConstants>
   </PropertyGroup>
...
```
コピペしてもいいですが、もともとある`<UseWindowsForms>`と`<TargetFramework>`は消して、`netX.X`をちゃんとしたバージョンに変えてください。

コードで使う例：
```example.cs
using System;

public IDialog CreateDialog(string message)
{
#if _WINDOWS_
    return new WindowsDialog(message) //Windowsでのみ実行される
#elif _MACOS_
    return new MacOSDialog(message)
#elif _LINUX_
    return new LinuxDialog(message)
#else
    throw new PlatformNotSupportedException(); //Windows,MacOS,Linuxのどれでもない場合にエラー
#endif
}
```
こんな感じで、OSごとのクラスと全体のインターフェースを作って、`#if`で分岐させるようにすればいいと思います。