Javaでは定数が呼び出し元に埋め込まれるが、.NET(C#)はどうか?
概要
あるdll内で定義されている定数を変更した場合、
呼び出し元を再コンパイルする必要はあるか?という質問。
常識的に考えたら、dllが変わったからといって、
呼び出し元を再コンパイルする必要はないのだが、
Javaでは、定数は呼び出し元に埋め込まれるため、
.NETではどうなのだろう、と考え出したのがきっかけ。
まず、Javaの挙動について実例を示す。
Main.javaからConstants.javaの定数を呼び出す場合
Constants.java
public interface Constants { public static final int NUM_CONST = 100; }
Main.java
public class Main { public static void main(String[] args) { System.out.println(Constants.NUM_CONST); } }
コンパイル&実行
>javac -version javac 1.5.0_10 >javac Main.java >java Main 100
当然のことながら、100が表示される。
次に、定数を200に変更して、Constants.javaのみコンパイルする。(やや恣意的だが)
Constants.java
public interface Constants { public static final int NUM_CONST = 200; }
コンパイル&実行
>javac Constants.java >java Main 100
定数を変更したにも関わらず、
変更前の100が表示されてしまう。
これは、最適化の関係で、呼び出し元に定数が埋め込まれてしまうためである。
逆コンパイル
>javap -c Main Compiled from "Main.java" public class Main extends java.lang.Object{ public Main(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: bipush 100 5: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 8: return }
改めて呼び出し元もコンパイルし直す必要がある。
コンパイル&実行
>javac Main.java >java Main 200
逆コンパイル
>javap -c Main Compiled from "Main.java" public class Main extends java.lang.Object{ public Main(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: sipush 200 6: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 9: return }
Main.javaからconst.jarの定数を呼び出す場合
ここまで書いて、外部提供のライブラリとかだったら困るんじゃね?
と思い、Constantsをjarにしてみたらどうかというのを試してみることに。
フォルダ構成
>tree /f │ Main.java │ └─lib Constants.java
Constants.java
public interface Constants { public static final int NUM_CONST = 100; }
コンパイル(サブ)&jar
>javac lib\Constants.java >jar cvf const100.jar -C lib Constants.class マニフェストが追加されました。 Constants.class を追加中です。(入 = 152) (出 = 126)(17% 収縮されました)
コンパイル(メイン)
>javac -classpath const100.jar Main.java
Constants.java
public interface Constants { public static final int NUM_CONST = 200; }
コンパイル(サブ)&jar
>javac lib\Constants.java >jar cvf const200.jar -C lib Constants.class マニフェストが追加されました。 Constants.class を追加中です。(入 = 152) (出 = 126)(17% 収縮されました)
実行
>java -classpath const200.jar;. Main 100 >
逆コンパイル
>javap -c Main Compiled from "Main.java" public class Main extends java.lang.Object{ public Main(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: bipush 100 5: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 8: return }
というわけで、別にjarであるからといって、定数埋め込みの最適化が
行われなくなるということはありませんでした。
つまり、Javaでは常にフルビルドが必須ということだけ覚えておけばよい。
ということで良さそうです。
C#ではどうか
Constant.cs
public class Constant { public const int NUM_CONST = 100; }
Main.cs
using System; class Test { static void Main() { Console.WriteLine(Constant.NUM_CONST); } }
コンパイル(サブ)
>csc /target:library Constant.cs Microsoft(R) Visual C# .NET Compiler version 7.10.6001.4 for Microsoft(R) .NET Framework version 1.1.4322 Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
コンパイル(メイン)
>csc /reference:Constant.dll Main.cs Microsoft(R) Visual C# .NET Compiler version 7.10.6001.4 for Microsoft(R) .NET Framework version 1.1.4322 Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
実行
>Main 100
Constant.cs
次、定数を200に変更してdll再作成。
public class Constant { public const int NUM_CONST = 200; }
コンパイル(サブ)
>csc /target:library Constant.cs Microsoft(R) Visual C# .NET Compiler version 7.10.6001.4 for Microsoft(R) .NET Framework version 1.1.4322 Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
実行
>Main 100
うーむ、同じことが起こっているなあ。。
コンパイル(メイン)
>csc /reference:Constant.dll Main.cs Microsoft(R) Visual C# .NET Compiler version 7.10.6001.4 for Microsoft(R) .NET Framework version 1.1.4322 Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
実行
>Main 200
念のため
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.3053 for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727 Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
でもやってみましたが、同じ結果でした。