'var'を使用するとパフォーマンスに影響がありますか?


230

以前、私はなぜvarキーワードが多くの例で見られるのかについて質問しましたが、匿名型にのみ必要であるにもかかわらず、それでもコードを「より速く」/「より簡単に」書くために使用されるという答えを得ました。

続いて、このリンク(「C#3.0 -ヴァールされていないOBJEC」) IソーvarIL内の正しい種類までコンパイルされる(あなたは記事下のほぼ中間に表示されます)。

私の質問は、もしあれば、ILコードがvarキーワードtake をどれだけ使用するかであり、どこで使用しても、コードのパフォーマンスを測定可能なレベルに近づけることになるでしょうか?


1
質問は何年も前に回答されましたが、varに対してもう1つ追加したいだけです-コンパイル時に解決されますが、Visual Studioの「Find All References」およびResharperの「Find Usages」では、タイプのすべての使用法を検索したい場合、適切に特定されません。 -そして、それは遅すぎるので修正されません。
KolA、2015年

@KolA変数で宣言された変数はvar、Visual Studio 2019の「すべての参照を検索」で最も確実に機能するため、壊れた場合は修正されました。しかし、私はそれがVisual Studio 2012までさかのぼって動作することを確認できたので、なぜ動作しなかったと主張したのかはわかりません。
Herohtar

@Herohtarは、コード「class X {} X GetX(){return new X();} void UseX(){var x = GetX();}」を試して、Xへのすべての参照を検索します。「var x = GetX( ) "ビットは強調表示されていません-現在の最新のVS2019では、これが私が意図したことです。ただし、varの代わりに "X x = GetX()"を使用すると強調表示されます
KolA

1
@KolAああ、私はあなたが何を意味するのかを理解しています-で「すべての参照を検索」を使用varするXときの参照とは見なされませんX。興味深いことに、varそのステートメントで「すべての参照を検索」を使用する、参照表示されますX(ただし、varステートメントはリストされません)。さらに、カーソルをに合わせると、同じドキュメント内のvarすべてのインスタンスが強調表示さXれます(逆も同様です)。
Herohtar

回答:


316

varキーワードに追加のILコードはありません。結果のILは、非匿名型と同じでなければなりません。使用するつもりのタイプがわからないためにコンパイラーがそのILを作成できない場合、コンパイラー・エラーが発生します。

唯一のトリックはvar、タイプを手動で設定した場合にインターフェースまたは親タイプを選択した可能性がある正確なタイプを推測することです。


8年後の更新

私の理解が変わったので、これを更新する必要があります。varメソッドがインターフェースを返す状況ではパフォーマンスに影響を与える可能性があると私は今信じていますが、あなたは正確なタイプを使用したでしょう。たとえば、このメソッドがある場合:

IList<int> Foo()
{
    return Enumerable.Range(0,10).ToList();
}

メソッドを呼び出すには、次の3行のコードを検討してください。

List<int> bar1 = Foo();
IList<int> bar = Foo();
var bar3 = Foo();

3つすべてが期待どおりにコンパイルおよび実行されます。ただし、最初の2行は完全に同じではなく、3行目は1行目ではなく2行目と一致します。のシグネチャFoo()はを返すことなIList<int>ので、これがコンパイラがbar3変数を構築する方法です。

パフォーマンスの観点からは、ほとんど気付かないでしょう。ただし、3行目のパフォーマンスが1行目のパフォーマンスほど速くない場合がありますbar3変数を使い続けると、コンパイラーは同じ方法でメソッド呼び出しをディスパッチできない場合があります。

ジッタがこの差を消す可能性がある(たぶん均等になる)ことに注意してください。ただし、保証はされていません。一般に、varパフォーマンスの点では依然として非要因であると考える必要があります。確かに、dynamic変数を使用するようなものではありません。しかし、それがまったく違いをもたらさないと言うことは、それを過大評価するかもしれません。


23
ILが同一であるだけでなく、同一でもあります。var i = 42; int i = 42とまったく同じコードにコンパイルされます。
ブライアンラスムッセン

15
@BrianRasmussen:あなたの投稿が古いことは知っていますが、var i = 42;(infersタイプはint)はと同じではないと思いlong i = 42;ます。したがって、場合によっては、型推論について誤った仮定をしている可能性があります。これは、値が適合しない場合、とらえどころのない/エッジケースのランタイムエラーを引き起こす可能性があります。そのため、値に明示的な型がない場合でも、明示的にすることをお勧めします。したがって、たとえば、var x = new List<List<Dictionary<int, string>()>()>()許容できますが、var x = 42ややあいまいであり、と記述する必要がありますint x = 42。しかし、それぞれに...
ネルソンロザーメル

50
@NelsonRothermel:あいまいでvar x = 42; はありません。整数リテラルは型intです。あなたが文字通りの長いものが欲しいなら、あなたは書くvar x = 42L;
Brian Rasmussen

6
ILはC#で何を表していますか。聞いたことがない。
puretppc '19年

15
動作が異なる3行のコードの例では、最初の行はコンパイルされません。両方の2行目と3行目、やる、コンパイルはまったく同じことを行います。の代わりにをFoo返した場合ListIList3行すべてがコンパイルされますが、3行目は2 行目ではなく1行と同じように動作します
サービス

72

Joelが言うように、コンパイラーはコンパイル時に var型がどうあるべきかを計算します。事実上、それはコンパイラーがキーストロークを保存するために実行するトリックにすぎません。

var s = "hi";

に置き換えられます

string s = "hi";

ILが生成される前にコンパイラーによって。生成されたILは、文字列を入力した場合とまったく同じです。


26

まだ誰もリフレクターについて言及していないので...

次のC#コードをコンパイルすると、

static void Main(string[] args)
{
    var x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

次に、リフレクターを使用すると、次のようになります。

// Methods
private static void Main(string[] args)
{
    string x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

したがって、答えは明らかに実行時のパフォーマンスへの影響がないことです!


17

次のメソッドの場合:

   private static void StringVsVarILOutput()
    {
        var string1 = new String(new char[9]);

        string string2 = new String(new char[9]);
    }

IL出力はこれです:

        {
          .method private hidebysig static void  StringVsVarILOutput() cil managed
          // Code size       28 (0x1c)
          .maxstack  2
          .locals init ([0] string string1,
                   [1] string string2)
          IL_0000:  nop
          IL_0001:  ldc.i4.s   9
          IL_0003:  newarr     [mscorlib]System.Char
          IL_0008:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_000d:  stloc.0
          IL_000e:  ldc.i4.s   9
          IL_0010:  newarr     [mscorlib]System.Char
          IL_0015:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_001a:  stloc.1
          IL_001b:  ret
        } // end of method Program::StringVsVarILOutput

14

C#コンパイラは、varコンパイル時に変数の真の型を推測します。生成されたILに違いはありません。


14

つまり、明確に言うと、遅延コーディングスタイルです。私は選択を考えると、ネイティブ型を好みます。この余分な「ノイズ」を取り、コード/デバッグ時に自分が考えていることを正確に読み書きできるようにします。*肩をすくめる*


1
これは主観的な見方であり、パフォーマンスに関する質問への答えではありません。正解は、パフォーマンスには影響しないということです。私は
アンデルス

これはvarパフォーマンスに影響を与えるかどうかの質問にはまったく答えません。あなたは人々がそれを使うべきかどうかについてあなたの意見を述べているだけです。
Herohtar

後で値から型を推測する(たとえば、int 5からfloat 5.25に切り替える)と、パフォーマンスの問題が絶対に発生する可能性があります。*肩をすくめる*
ChrisH

いいえ、それはパフォーマンスの問題を引き起こしません。intは自動的にを変換できないため、型の変数が必要な場所でビルドエラーfloatが発生しますが、明示的に使用intしてに変更した場合とまったく同じですfloat。いずれにせよ、あなたの答えは「使用varはパフォーマンスに影響を与えますか?」という質問にはまだ答えません。(特に生成されたILの観点から)
Herohtar

8

読んだ内容をきちんと理解していないと思います。正しいタイプにコンパイルされる場合、違いはありません。私がこれをするとき:

var i = 42;

コンパイラーはそれがintであることを認識し、私が書いたかのようにコードを生成します

int i = 42;

あなたがリンクした投稿が言うように、それは同じタイプにコンパイルされます。これは、ランタイムチェックやその他のコードを必要とするものではありません。コンパイラは、型が何である必要があるかを理解し、それを使用します。


そうですが、後でi = i-someVarおよびsomeVar = 3.3の場合はどうでしょう。私は今、Intです。コンパイラーが欠陥を見つけることから先に開始できるようにするだけでなく、実行時エラーやプロセスを遅くする型変換を最小限に抑えるためにも明示的にすることをお勧めします。*肩をすくめる*また、コードを自己記述的に改善します。私は長い間、これをやってきました。選択が与えられれば、毎回明示的な型を持つ「ノイズの多い」コードを使用します。
ChrisH

5

varを使用しても、実行時のパフォーマンスコストはありません。ただし、コンパイラは型を推測する必要があるため、コンパイルのパフォーマンスコストがあると思いますが、これはほとんど無視できます。


10
とにかく、RHSはその型を計算する必要があります-コンパイラーは不一致の型をキャッチしてエラーをスローするので、実際にはコストはかかりません。
ジミー

3

コンパイラが自動型推論を実行できる場合、パフォーマンスに問題はありません。これらは両方とも同じコードを生成します

var    x = new ClassA();
ClassA x = new ClassA();

ただし、型を動的に構築する場合(LINQ ...)varは唯一の質問であり、ペナルティとは何かを比較するために比較する他のメカニズムがあります。


3

私は常にウェブ記事やガイドの文章でvarという言葉を使用しています。

オンライン記事のテキストエディターの横幅が狭いです。

これを書けば:

SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

上記のレンダリングされたプリコードテキストが長すぎてボックスから流れ出し、非表示になることがわかります。完全な構文を表示するには、リーダーを右にスクロールする必要があります。

そのため、ウェブ記事の執筆では常にvarというキーワードを使用しています。

var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

レンダリングされたプレコード全体が画面にぴったり収まります。

実際には、オブジェクトを宣言するためにvarを使用することはめったになく、オブジェクトをより速く宣言するためにインテリセンスに依存しています。

例:

SomeCoolNamespace.SomeCoolObject coolObject = new SomeCoolNamespace.SomeCoolObject();

しかし、メソッドからオブジェクトを返すために、私はvarを使用してより速くコードを記述しています。

例:

var coolObject = GetCoolObject(param1, param2);

学生向けに書いている場合は、自分のドッグフードを食べ、常に同じ「正しい」方法で一貫して書いてください。生徒は物事を100%逐語的かつ真剣に受け止め、途中で拾ったずさんな習慣を使い始めます。$ .02
ChrisH 2017年

1

"var"は、人々が愛したり嫌ったりするものの1つです(地域など)。ただし、リージョンとは異なり、匿名クラスを作成するときにはvarが絶対に必要です。

私にとって、varは次のようにオブジェクトを直接更新するときに意味があります。

var dict = new Dictionary<string, string>();

そうは言っても、簡単に行うことができます:

Dictionary<string, string> dict = newおよびintellisenseが残りの部分をここに入力します。

特定のインターフェイスのみを操作する場合は、呼び出すメソッドが直接インターフェイスを返さない限り、varを使用できません。

Resharperは、「var」を全体的に使用する側にいるようです。そのため、より多くの人々にそうするように強いられる可能性があります。しかし、メソッドを呼び出している場合は読みづらく、名前によって何が返されるのかが明確ではないことにも同意します。

var自体は速度を落とすことはありませんが、多くの人が考えていないことの1つ注意点があります。その場合はvar result = SomeMethod();、その後のコードでは、さまざまなメソッドやプロパティを呼び出すか、どんな思い結果背中のいくつかの並べ替えを期待しています。場合はSomeMethod()他のいくつかのタイプに、その定義を変更したが、それはまだ他のコードを期待していた契約に会った、あなただけ(無単位/統合テストであれば、当然の)本当に厄介なバグを作成しました。


0

それは状況に依存します、あなたが使用しようとすると、このコードは怒鳴ります。

表現が「OBJECT」に変換され、パフォーマンスが大幅に低下しますが、それは孤立した問題です。

コード:

public class Fruta
{
    dynamic _instance;

    public Fruta(dynamic obj)
    {
        _instance = obj;
    }

    public dynamic GetInstance()
    {
        return _instance;
    }
}

public class Manga
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
    public int MyProperty3 { get; set; }
}

public class Pera
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
}

public class Executa
{
    public string Exec(int count, int value)
    {
        int x = 0;
        Random random = new Random();
        Stopwatch time = new Stopwatch();
        time.Start();

        while (x < count)
        {
            if (value == 0)
            {
                var obj = new Pera();
            }
            else if (value == 1)
            {
                Pera obj = new Pera();
            }
            else if (value == 2)
            {
                var obj = new Banana();
            }
            else if (value == 3)
            {
                var obj = (0 == random.Next(0, 1) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance());
            }
            else
            {
                Banana obj = new Banana();
            }

            x++;
        }

        time.Stop();
        return time.Elapsed.ToString();
    }

    public void ExecManga()
    {
        var obj = new Fruta(new Manga()).GetInstance();
        Manga obj2 = obj;
    }

    public void ExecPera()
    {
        var obj = new Fruta(new Pera()).GetInstance();
        Pera obj2 = obj;
    }
}

上記のILSPYの結果。

public string Exec(int count, int value)
{
    int x = 0;
    Random random = new Random();
    Stopwatch time = new Stopwatch();
    time.Start();

    for (; x < count; x++)
    {
        switch (value)
        {
            case 0:
                {
                    Pera obj5 = new Pera();
                    break;
                }
            case 1:
                {
                    Pera obj4 = new Pera();
                    break;
                }
            case 2:
                {
                    Banana obj3 = default(Banana);
                    break;
                }
            case 3:
                {
                    object obj2 = (random.Next(0, 1) == 0) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance();
                    break;
                }
            default:
                {
                    Banana obj = default(Banana);
                    break;
                }
        }
    }
time.Stop();
return time.Elapsed.ToString();
}

このコードを実行したい場合は、以下のコードを使用して、時間の差を取得してください。

        static void Main(string[] args)
    {
        Executa exec = new Executa();            
        int x = 0;
        int times = 4;
        int count = 100000000;
        int[] intanceType = new int[4] { 0, 1, 2, 3 };

        while(x < times)
        {                
            Parallel.For(0, intanceType.Length, (i) => {
                Console.WriteLine($"Tentativa:{x} Tipo de Instancia: {intanceType[i]} Tempo Execução: {exec.Exec(count, intanceType[i])}");
            });
            x++;
        }

        Console.ReadLine();
    }

よろしく

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.