csc は嘘つき
csc は、最近の Windows に標準搭載されている C# のコンパイラである。
実行ファイルは
C:\Windows\Microsoft.NET\Framework\v4.0.30319
などにあるので、事前にパスを通しておく。
例として、適当なライブラリを書く。
public class TestLib
{
static int Func(int x)
{
return x + 30;
}
}
Func
に public
がついていないのはわざとである。
このライブラリをコンパイルする。
csc /target:library /optimize+ TestLib.cs
このライブラリを使うためのコードを書く。
class Test
{
public static void Main()
{
System.Console.WriteLine(TestLib.Func(42));
}
}
コンパイルを試みる。
csc /optimize+ /reference:TestLib.dll Test.cs
すると、以下のメッセージが出力された。
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 でコンパイルしたライブラリの中身を確認すると、確かに Func
の定義が含まれていることがわかる。
C# フィールドやprivateメソッドを無理やり使用する #reflection - Qiita
を参考に、Func
を呼び出すコードを書く。
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 }));
}
}
コンパイルする。
csc /optimize+ Test2.cs
すると、コンパイルが通り、実行すると期待した通り 72
が出力された。
まず、Type.GetType(string)
で、ライブラリのクラスの型を取得する。
引数は クラス名,ファイル(アセンブリ)名
の形式で指定するとよいようである。
続いて、Type.GetMethod(String, BindingFlags)
で、呼び出すメソッドを取得する。
今回は静的メソッドを呼び出すので、それに合わせて BindingFlags
を設定する。
最後に、MethodInfo.Invoke(Object, Object[])
で、メソッドを呼び出す。
今回は静的メソッドを呼び出すので、インスタンスを指定する第1引数は適当でよい (無視される)。
このように、実際に存在し、呼び出すことも可能なメソッドについて「ありません」と主張してくる csc は嘘つきである。
javac は正直
同じことを Java で試してみる。
コンパイラのバージョンは javac 21.0.1
である。
Java では、jar に分けるだけではメソッドが使えてしまったので、ライブラリのクラスをパッケージに入れる。
package testlib;
public class TestLib {
static int func(int x) {
return x + 30;
}
}
コンパイルして jar にする。
javac testlib\TestLib.java
jar -cf TestLib.jar testlib\TestLib.class
(.class を直接参照しないように) 別のディレクトリで、ライブラリを使うためのコードを書く。
public class Test {
public static void main(String[] args) {
System.out.println(testlib.TestLib.func(42));
}
}
コンパイルを試みる。
javac -cp testlib_java\TestLib.jar Test.java
すると、以下のメッセージが出力された。
Test.java:3: エラー: TestLibのfunc(int)はpublicではありません。パッケージ外からはアクセスできません
System.out.println(testlib.TestLib.func(42));
^
エラー1個
「ありません」という嘘をついてきた csc と違って、(今回用いたバージョンの) javac は正直に「アクセスできません」と伝えてきた。