csc は嘘つき あるものを無いと主張してきやがる

# csc は嘘つき

csc は、最近の Windows に標準搭載されている C# のコンパイラである。
実行ファイルは

```text
C:\Windows\Microsoft.NET\Framework\v4.0.30319
```

などにあるので、事前にパスを通しておく。

例として、適当なライブラリを書く。

```csharp:TestLib.cs
public class TestLib
{
	static int Func(int x)
	{
		return x + 30;
	}
}
```

:::note info
`Func` に `public` がついていないのはわざとである。
:::

このライブラリをコンパイルする。

```bat
csc /target:library /optimize+ TestLib.cs
```

このライブラリを使うためのコードを書く。

```csharp:Test.cs
class Test
{
	public static void Main()
	{
		System.Console.WriteLine(TestLib.Func(42));
	}
}
```

コンパイルを試みる。

```bat
csc /optimize+ /reference:TestLib.dll Test.cs
```

すると、以下のメッセージが出力された。

```text
Microsoft (R) Visual C# Compiler version 4.8.9232.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

Test.cs(5,36): error CS0117: 'TestLib' に 'Func' の定義がありません。
```

`TestLib` に確かに `Func` の定義を書いているはずなのに、「'TestLib' に 'Func' の定義がありません」と主張してきている。
**これは明らかに嘘である。**
[ILSpy](https://github.com/icsharpcode/ILSpy) でコンパイルしたライブラリの中身を確認すると、確かに `Func` の定義が含まれていることがわかる。

![ILSpy によるライブラリの内容の確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/54fae6d0-c620-26b6-1661-74bea592b820.png)

[C# フィールドやprivateメソッドを無理やり使用する #reflection - Qiita](https://qiita.com/Aki_Pine1024/items/f6a8a801d71312275655)
を参考に、`Func` を呼び出すコードを書く。

```csharp:Test2.cs
using System;
using System.Reflection;

class Test2
{
	public static void Main()
	{
		Type libType = Type.GetType("TestLib,TestLib");
		if (libType == null) {
			Console.WriteLine("libType is null");
			return;
		}
		MethodInfo method = libType.GetMethod(
			"Func",
			BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic
		);
		if (method == null) {
			Console.WriteLine("method is null");
			return;
		}
		Console.WriteLine(method.Invoke(null, new object[]{ 42 }));
	}
}
```

コンパイルする。

```bat
csc /optimize+ Test2.cs
```

すると、コンパイルが通り、実行すると期待した通り `72` が出力された。

まず、[`Type.GetType(string)`](https://learn.microsoft.com/ja-jp/dotnet/api/system.type.gettype?view=net-8.0#system-type-gettype(system-string)) で、ライブラリのクラスの型を取得する。
引数は `クラス名,ファイル(アセンブリ)名` の形式で指定するとよいようである。

続いて、[`Type.GetMethod(String, BindingFlags)`](https://learn.microsoft.com/ja-jp/dotnet/api/system.type.getmethod?view=net-8.0#system-type-getmethod(system-string-system-reflection-bindingflags)) で、呼び出すメソッドを取得する。
今回は静的メソッドを呼び出すので、それに合わせて [`BindingFlags`](https://learn.microsoft.com/ja-jp/dotnet/api/system.reflection.bindingflags?view=net-8.0) を設定する。

最後に、[`MethodInfo.Invoke(Object, Object[])`](https://learn.microsoft.com/ja-jp/dotnet/api/system.reflection.methodbase.invoke?view=net-8.0#system-reflection-methodbase-invoke(system-object-system-object())) で、メソッドを呼び出す。
今回は静的メソッドを呼び出すので、インスタンスを指定する第1引数は適当でよい (無視される)。

このように、実際に存在し、呼び出すことも可能なメソッドについて「ありません」と主張してくる **csc は嘘つき**である。

# javac は正直

同じことを Java で試してみる。
コンパイラのバージョンは `javac 21.0.1` である。

Java では、jar に分けるだけではメソッドが使えてしまったので、ライブラリのクラスをパッケージに入れる。

```java:testlib/TestLib.java
package testlib;

public class TestLib {
	static int func(int x) {
		return x + 30;
	}
}
```

コンパイルして jar にする。

```bat
javac testlib\TestLib.java
jar -cf TestLib.jar testlib\TestLib.class
```

(.class を直接参照しないように) 別のディレクトリで、ライブラリを使うためのコードを書く。

```java:Test.java
public class Test {
	public static void main(String[] args) {
		System.out.println(testlib.TestLib.func(42));
	}
}
```

コンパイルを試みる。

```bat
javac -cp testlib_java\TestLib.jar Test.java
```

すると、以下のメッセージが出力された。

```text
Test.java:3: エラー: TestLibのfunc(int)はpublicではありません。パッケージ外からはアクセスできません
                System.out.println(testlib.TestLib.func(42));
                                                  ^
エラー1個
```

「ありません」という嘘をついてきた csc と違って、(今回用いたバージョンの) javac は**正直に「アクセスできません」と伝えてきた。**