web-dev-qa-db-ja.com

メソッド内で変数の代わりにconstを使用する利点

メソッドにローカル変数があるときはいつでも、ReSharperはそれらを定数に変換することを提案します。

// instead of this:
var s = "some string";
var flags = BindingFlags.Public | BindingFlags.Instance;

// ReSharper suggest to use this:
const string s = "some string";
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

これらは実際には定数値であり(変数ではない)、ReSharperがconstに変更することを提案していることを理解しています。

しかし、それとは別に、const BindingFlags便利で読みやすいvarキーワードの代わりに?

ところで、私はここで同様の質問を見つけました: Resharperは常に文字列ではなくconst文字列を作成することを私に提案します ですが、私の質問はローカル変数/定数に関するクラスのフィールドに関するものだと思います。

70
M4N

定数に値を割り当てようとすると、コンパイラはエラーをスローするため、誤って値を変更することを防ぐことができます。

また、通常、定数と変数を使用すると、パフォーマンスがわずかに向上します。これは、MSILにコンパイルされる方法に関係しています このMSDNマガジンQ&A

これで、コードでmyIntが参照されている場所では、変数から値を取得するために「ldloc.0」を実行する代わりに、MSILはMSILにハードコードされた定数値をロードするだけです。 そのため、通常、定数を使用することでパフォーマンスとメモリの利点はわずかになります。ただし、それらを使用するには、コンパイル時に変数の値が必要です。コンパイル時のこの定数への参照は、たとえそれらが別のアセンブリ内にある場合でも、この置換が行われます。

コンパイル時に値を知っている場合、定数は確かに便利なツールです。しないが、変数が1回だけ設定されるようにする場合は、C#のreadonlyキーワード(MSILのinitonlyにマップされる)を使用して、変数の値をコンストラクターでのみ設定できることを示すことができます。その後、変更するとエラーになります。これは、フィールドがクラスの識別を決定するのに役立つ場合によく使用され、多くの場合、コンストラクターパラメーターと同じに設定されます。

82
landoncz

tl; drリテラル値を持つローカル変数の場合、constはまったく違いはありません。


「内部メソッド」の区別は非常に重要です。それを見て、constフィールドと比較してみましょう。

定数ローカル変数

constローカル変数のonly利点は、値を再割り当てできないことです。

ただし、constはプリミティブ型(intdouble、...)およびstringに制限され、その適用性が制限されます。

余談:C#コンパイラには、「読み取り専用」ローカル( here )のより一般的な概念を許可する提案があり、この利点は他のシナリオにも拡張されます。ただし、おそらくconstとは見なされず、そのような宣言には別のキーワード(つまり、letまたはreadonly varまたはそのようなもの)。

次の2つの方法を検討してください。

private static string LocalVarString()
{
    var s = "hello";
    return s;
}

private static string LocalConstString()
{
    const string s = "hello";
    return s;
}

Releaseモードで構築された次の(短縮された)ILが表示されます。

.method private hidebysig static string LocalVarString() cil managed 
{
    ldstr        "hello"
    ret          
}

.method private hidebysig static string LocalConstString() cil managed 
{
    ldstr        "hello"
    ret          
}

ご覧のとおり、どちらもまったく同じILを生成します。ローカルのsconstであるかどうかは影響しません。

同じことがプリミティブ型にも当てはまります。 intを使用した例を次に示します。

private static int LocalVarInt()
{
    var i = 1234;
    return i;
}

private static int LocalConstInt()
{
    const int i = 1234;
    return i;
}

そして再び、IL:

.method private hidebysig static int32 LocalVarInt() cil managed
{
    ldc.i4       1234
    ret          
}

.method private hidebysig static int32 LocalConstInt() cil managed
{
    ldc.i4       1234
    ret     
}

繰り返しますが、違いはありません。ここではパフォーマンスやメモリの違いはありません。唯一の違いは、開発者がシンボルを再割り当てできないことです。

定数フィールド

constフィールドと変数フィールドの比較は異なります。非constフィールドは実行時に読み取る必要があります。したがって、次のようなILになります。

// Load a const field
ldc.i4       1234

// Load a non-const field
ldsfld       int32 MyProject.MyClass::_myInt

JITが定数値自体をインライン化できないと仮定すると、これがどのようにパフォーマンスの違いをもたらす可能性があるかは明らかです。

ここでのもう1つの重要な違いは、アセンブリ間で共有されるパブリックconstフィールドです。 1つのアセンブリがconstフィールドを公開し、別のアセンブリがそれを使用する場合、そのフィールドの実際の値はコンパイル時にコピーされます。これは、constフィールドを含むAssemblyが更新されても、使用しているAssemblyが再コンパイルされない場合、古い(および場合によっては誤った)値が使用されることを意味します。

定数式

次の2つの宣言を検討してください。

const int i = 1 + 2;
int i = 1 + 2;

const形式の場合、加算はコンパイル時に計算する必要があります。つまり、3がILに保持されます。

const以外の形式の場合、コンパイラはILで加算演算を自由に発行できますが、JITは基本的な定数の折りたたみ最適化をほぼ確実に適用するため、生成されるマシンコードは同一になります。

C#7.3コンパイラはldc.i4.3 opcode 上記の両方の式に対して。

21
Drew Noakes

私の理解では、Const値は実行時には存在しません-つまり、メモリの場所に保存された変数の形で-コンパイル時にMSILコードに埋め込まれます。したがって、パフォーマンスに影響を与えます。変数がこれらのチェックを必要とする場合、ハウスキーピング(変換チェック/ガベージコレクションなど)を実行するために、実行時を超える必要はありません。

14
YetAnotherUser

constはコンパイル時定数です。つまり、const変数を使用するすべてのコードは、const変数に含まれる定数式を含むようにコンパイルされます-放出されるILには、その定数値自体が含まれます。

つまり、定数では実行時にメモリを割り当てる必要がないため、メソッドのメモリフットプリントが小さくなります。

4
BrokenGlass

わずかなパフォーマンスの改善に加えて、定数を宣言するとき、自分自身とコードを使用する他の開発者に2つのルールを明示的に適用します。

  1. 今は値で初期化する必要があります。他の場所ではできません。
  2. どこでもその値を変更することはできません。

コードでは、読みやすさとコミュニケーションがすべてです。

3

Const値は、オブジェクトのすべてのインスタンス間で「共有」されます。メモリ使用量も少なくなる可能性があります。

例として:

public class NonStatic
{
    int one = 1;
    int two = 2;
    int three = 3;
    int four = 4;
    int five = 5;
    int six = 6;
    int seven = 7;
    int eight = 8;
    int nine = 9;
    int ten = 10;        
}

public class Static
{
    static int one = 1;
    static int two = 2;
    static int three = 3;
    static int four = 4;
    static int five = 5;
    static int six = 6;
    static int seven = 7;
    static int eight = 8;
    static int nine = 9;
    static int ten = 10;
}

.Netではメモリの消費が難しいため、詳細を理解するふりをするつもりはありませんが、100万個の「静的」のリストをインスタンス化すると、使用しないメモリよりもかなり少ないメモリを使用する可能性が高くなります。

    static void Main(string[] args)
    {
        var maxSize = 1000000;
        var items = new List<NonStatic>();
        //var items = new List<Static>();

        for (var i=0;i<maxSize;i++)
        {
            items.Add(new NonStatic());
            //items.Add(new Static());
        }

        Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64);
        Console.Read();
    }

「NonStatic」を使用する場合、ワーキングセットは静的を使用する場合の32,423,936と比較して69,398,528です。

2
Rob P.

Constキーワードは、コンパイル時に完全に評価できることをコンパイラに伝えます。これにはパフォーマンスとメモリの利点がありますが、小さいです。

1
Chris Trombley

C#の定数は、データ値を保存するためにメモリ内の名前の場所を提供します。これは、変数の値がコンパイル時に認識され、単一の場所に格納されることを意味します。

宣言すると、Microsoft Intermediate Language(MSIL)で一種の「ハードコーディング」されます。

少しですが、コードのパフォーマンスを改善できます。変数を宣言していて、それをconstにできる場合は、常にそれを行います。パフォーマンスを改善できるからだけでなく、それが定数のアイデアだからです。そうでなければ、なぜ存在するのですか?

Reflectorは、このような状況で本当に役立ちます。変数を宣言してから定数にし、[〜#〜] il [〜#〜]で生成されるコードを確認してください。あとは、指示の違いを確認し、それらの指示の意味を確認するだけです。

1
Oscar Mederos