Beeinträchtigt die Verwendung von 'var' die Leistung?

230

Früher habe ich eine Frage gestellt, warum ich sehe, dass so viele Beispiele das varSchlüsselwort verwenden, und die Antwort erhalten, dass es zwar nur für anonyme Typen erforderlich ist, aber dennoch verwendet wird, um das Schreiben von Code "schneller" / einfacher und "nur weil" zu machen.

Im Anschluss an diesen Link ( „C # 3.0 - Var Is not Objec“) Ich sah , dass varauf den richtigen Typ nach unten in der IL kompiliert wird (Sie werden es etwa auf halbem Weg nach unten Artikels).

Meine Frage ist, wie viel mehr, wenn überhaupt, IL-Code mit dem varSchlüsselwort benötigt, und wäre es sogar annähernd messbar, wenn die Leistung des Codes überall verwendet würde?

Jeff Keslinke
quelle
1
Die Frage wurde vor langer Zeit beantwortet und wollte nur noch eine Sache gegen var hinzufügen - obwohl sie zur Kompilierungszeit gelöst wurde, wird sie von Visual Studios "Alle Referenzen suchen" und Resharpers "Verwendungen suchen" nicht richtig erkannt, wenn Sie alle Verwendungen des Typs finden möchten - und es wird nicht behoben, weil es zu langsam wäre.
KolA
@ KolA-Variablen, die mit deklariert wurden, varfunktionieren definitiv mit "Alle Referenzen suchen " in Visual Studio 2019. Wenn es also jemals kaputt gegangen ist, wurde es behoben. Ich kann jedoch bestätigen, dass es bereits in Visual Studio 2012 funktioniert. Daher bin ich mir nicht sicher, warum Sie behauptet haben, dass es nicht funktioniert hat.
Herohtar
@Herohtar versuchen Sie, den folgenden Code "Klasse X {} X GetX () {neues X () zurückgeben;} void UseX () {var x = GetX ();}" und suchen Sie alle Verweise auf X, das "var x = GetX ( ) "Bit wird nicht hervorgehoben - im neuesten VS2019 ist dies ab sofort gemeint. Es wird jedoch hervorgehoben, wenn Sie "X x = GetX ()" anstelle von var
KolA
1
@KolA Ah, ich verstehe, was Sie meinen - varwird nicht als Referenz angesehen, Xwenn Sie "Alle Referenzen suchen " verwenden X. Interessanterweise , wenn Sie „Alle Verweise finden“ verwenden , um auf varin dieser Aussage, es wird Ihnen Verweis zeigen X(auch wenn es immer noch nicht die Liste varAnweisung). Wenn der Cursor eingeschaltet ist var, werden außerdem alle Instanzen Xim selben Dokument hervorgehoben (und umgekehrt).
Herohtar

Antworten:

316

Es gibt keinen zusätzlichen IL-Code für das varSchlüsselwort: Die resultierende IL sollte für nicht anonyme Typen identisch sein. Wenn der Compiler diese IL nicht erstellen kann, weil er nicht herausfinden kann, welchen Typ Sie verwenden möchten, wird ein Compilerfehler angezeigt.

Der einzige Trick besteht darin var, einen genauen Typ abzuleiten, bei dem Sie möglicherweise eine Schnittstelle oder einen übergeordneten Typ ausgewählt haben, wenn Sie den Typ manuell festlegen.


Update 8 Jahre später

Ich muss dies aktualisieren, da sich mein Verständnis geändert hat. Ich glaube jetzt, dass es möglich sein kann var, die Leistung in der Situation zu beeinflussen, in der eine Methode eine Schnittstelle zurückgibt, aber Sie hätten einen genauen Typ verwendet. Wenn Sie beispielsweise diese Methode haben:

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

Betrachten Sie diese drei Codezeilen, um die Methode aufzurufen:

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

Alle drei werden wie erwartet kompiliert und ausgeführt. Die ersten beiden Zeilen sind jedoch nicht genau gleich, und die dritte Zeile entspricht eher der zweiten als der ersten. Da die Signatur von Foo()eine zurückgeben IList<int>soll, erstellt der Compiler die bar3Variable auf diese Weise.

Vom Standpunkt der Leistung aus werden Sie es meistens nicht bemerken. Es gibt jedoch Situationen, in denen die Leistung der dritten Zeile möglicherweise nicht ganz so schnell ist wie die Leistung der ersten . Wenn Sie die bar3Variable weiterhin verwenden , kann der Compiler möglicherweise Methodenaufrufe nicht auf die gleiche Weise versenden.

Beachten Sie, dass es möglich ist (wahrscheinlich sogar), dass der Jitter diesen Unterschied beseitigen kann, dies ist jedoch nicht garantiert. Im Allgemeinen sollten Sie immer noch davon ausgehen var, dass dies kein Leistungsfaktor ist. Es ist sicherlich überhaupt nicht so, als würde man eine dynamicVariable verwenden. Aber zu sagen, dass es überhaupt keinen Unterschied macht, kann es übertreiben.

Joel Coehoorn
quelle
23
Die IL sollte nicht nur identisch sein - sie ist auch identisch. var i = 42; Kompiliert mit genau demselben Code wie int i = 42;
Brian Rasmussen
15
@BrianRasmussen: Ich weiß, dass dein Beitrag alt ist, ist alt, aber ich gehe davon aus var i = 42;(Folgertyp ist int) ist NICHT identisch mit long i = 42;. In einigen Fällen machen Sie möglicherweise falsche Annahmen über die Typinferenz. Dies kann zu schwer fassbaren Laufzeitfehlern führen, wenn der Wert nicht passt. Aus diesem Grund ist es möglicherweise immer noch eine gute Idee, explizit zu sein, wenn der Wert keinen expliziten Typ hat. So wäre zum Beispiel var x = new List<List<Dictionary<int, string>()>()>()akzeptabel, ist aber var x = 42etwas mehrdeutig und sollte als geschrieben werden int x = 42. Aber für jeden sein eigenes ...
Nelson Rothermel
50
@ NelsonRothermel: var x = 42; ist nicht mehrdeutig. Ganzzahlige Literale sind vom Typ int. Wenn Sie eine wörtliche lange wollen, schreiben Sie var x = 42L;.
Brian Rasmussen
6
Ähm, wofür steht IL in C #? Ich habe nie wirklich davon gehört.
Puretppc
15
In Ihrem Beispiel für die 3 Codezeilen, die sich unterschiedlich verhalten, wird die erste Zeile nicht kompiliert . Die zweite und dritte Zeile, die beide tun Kompilierung, tun genau das Gleiche. Wenn Fooa Listanstelle von a zurückgegeben wird IList, werden alle drei Zeilen kompiliert, aber die dritte Zeile verhält sich wie die erste Zeile , nicht wie die zweite.
Servy
72

Wie Joel sagt, ermittelt der Compiler zur Kompilierungszeit, welcher Typ var sein soll. Tatsächlich ist dies nur ein Trick, den der Compiler ausführt, um beispielsweise Tastenanschläge zu speichern

var s = "hi";

wird ersetzt durch

string s = "hi";

vom Compiler, bevor eine IL generiert wird. Die generierte IL ist genau so, als hätten Sie eine Zeichenfolge eingegeben.

ljs
quelle
26

Da hat noch niemand Reflektor erwähnt ...

Wenn Sie den folgenden C # -Code kompilieren:

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

Dann verwenden Sie Reflektor, Sie erhalten:

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

Die Antwort ist also eindeutig kein Laufzeit-Performance-Hit!

RichardOD
quelle
17

Für die folgende Methode:

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

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

Die IL-Ausgabe lautet wie folgt:

        {
          .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
rauben
quelle
14

Der C # -Compiler leitet zur Kompilierungszeit den wahren Typ der varVariablen ab. Es gibt keinen Unterschied in der generierten IL.

Michael Burr
quelle
14

Um klar zu sein, es ist ein fauler Codierungsstil. Ich bevorzuge einheimische Typen, wenn ich die Wahl habe; Ich werde dieses zusätzliche bisschen "Rauschen" nehmen, um sicherzustellen, dass ich genau das schreibe und lese, was ich denke, dass ich zur Code- / Debug-Zeit bin. * Achselzucken *

ChrisH
quelle
1
Das ist nur Ihre subjektive Sichtweise und keine Antwort auf die Frage nach der Leistung. Die richtige Antwort ist, dass es keinen Einfluss auf die Leistung hat. Ich habe für nah gestimmt
Anders
Dies beantwortet nicht die Frage, ob vardie Leistung überhaupt beeinträchtigt wird. Sie geben nur Ihre Meinung dazu ab, ob die Leute es benutzen sollen.
Herohtar
Das spätere Ableiten des Typs vom Wert, z. B. das Wechseln von int 5 zu float 5.25, kann zu Leistungsproblemen führen. * Achselzucken *
ChrisH
Nein, das verursacht keine Leistungsprobleme. Sie erhalten Build-Fehler an allen Stellen, an denen eine Variable vom Typ erwartet wurde, intda diese nicht automatisch konvertiert werden kann. Dies floatist jedoch genau das Gleiche, das passieren würde, wenn Sie sie explizit verwenden intund dann ändern würden float. In jedem Fall beantwortet Ihre Antwort die Frage " varBeeinträchtigt die Verwendung die Leistung?" Immer noch nicht. (insbesondere in Bezug auf generierte IL)
Herohtar
8

Ich glaube, Sie haben nicht richtig verstanden, was Sie gelesen haben. Wenn es auf den richtigen Typ kompiliert wird, gibt es keinen Unterschied. Wenn ich das mache:

var i = 42;

Der Compiler weiß, dass es sich um ein Int handelt, und generiert Code, als hätte ich geschrieben

int i = 42;

Wie der Beitrag, auf den Sie verlinkt haben, besagt, wird er auf denselben Typ kompiliert . Es ist keine Laufzeitprüfung oder irgendetwas anderes, das zusätzlichen Code erfordert. Der Compiler findet nur heraus, welcher Typ sein muss, und verwendet diesen.

jalf
quelle
Richtig, aber was ist, wenn Sie später i = i - someVar und someVar = 3.3. Ich bin jetzt ein Int. Es ist besser, explizit zu sein, um dem Compiler nicht nur einen Vorsprung beim Auffinden von Fehlern zu verschaffen, sondern auch um Laufzeitfehler oder prozessverlangsamende Typkonvertierungen zu minimieren. * Achselzucken * Es macht den Code auch besser für die Selbstbeschreibung. Ich mache das schon lange, lange. Ich werde jedes Mal "verrauschten" Code mit expliziten Typen nehmen, wenn ich die Wahl habe.
ChrisH
5

Die Verwendung von var verursacht keine Laufzeitleistungskosten. Ich würde jedoch vermuten, dass es Kosten für die Kompilierungsleistung gibt, da der Compiler auf den Typ schließen muss, obwohl dies höchstwahrscheinlich vernachlässigbar sein wird.

Brian Rudolph
quelle
10
Der Typ der RHS muss sowieso berechnet werden - der Compiler würde nicht übereinstimmende Typen abfangen und einen Fehler auslösen, also keine wirklichen Kosten, denke ich.
Jimmy
3

Wenn der Compiler eine automatische Typinferenz durchführen kann, gibt es keine Probleme mit der Leistung. Beide generieren denselben Code

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

Wenn Sie den Typ jedoch dynamisch konstruieren (LINQ ...), varist dies Ihre einzige Frage, und es gibt einen anderen Mechanismus zum Vergleichen, um zu sagen, was die Strafe ist.

bläulich
quelle
3

Ich verwende immer das Wort var in Webartikeln oder Leitfäden.

Die Breite des Texteditors des Online-Artikels ist gering.

Wenn ich das schreibe:

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

Sie werden sehen, dass der oben gerenderte Vorcode-Text zu lang ist und sofort ausgeblendet wird. Der Leser muss nach rechts scrollen, um die vollständige Syntax anzuzeigen.

Deshalb verwende ich in Webartikel-Schriften immer das Schlüsselwort var.

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

Der gesamte gerenderte Vorcode passt einfach in den Bildschirm.

In der Praxis verwende ich zum Deklarieren von Objekten selten var. Ich verlasse mich auf Intellisense, um Objekte schneller zu deklarieren.

Beispiel:

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

Für die Rückgabe von Objekten aus einer Methode verwende ich var, um Code schneller zu schreiben.

Beispiel:

var coolObject = GetCoolObject(param1, param2);
mjb
quelle
Wenn Sie für Schüler schreiben, dann essen Sie Ihr eigenes Hundefutter und schreiben Sie es immer auf die gleiche "richtige" Weise, konsequent. Die Schüler nehmen die Dinge oft zu 100% wörtlich und zu Herzen und beginnen, schlampige Gewohnheiten anzuwenden, die sie auf dem Weg lernen. $
.02
1

"var" ist eines der Dinge, die Menschen entweder lieben oder hassen (wie Regionen). Im Gegensatz zu Regionen ist var beim Erstellen anonymer Klassen unbedingt erforderlich.

Für mich ist var sinnvoll, wenn Sie ein Objekt direkt wie folgt neu erstellen:

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

Davon abgesehen können Sie ganz einfach Folgendes tun:

Dictionary<string, string> dict = Neu und Intellisense wird den Rest für Sie hier ausfüllen.

Wenn Sie nur mit einer bestimmten Schnittstelle arbeiten möchten, können Sie var nur verwenden, wenn die von Ihnen aufgerufene Methode die Schnittstelle direkt zurückgibt.

Resharper scheint auf der Seite der Verwendung von "var" zu stehen, was dazu führen kann, dass mehr Leute dies tun. Aber ich stimme zu, dass es schwieriger zu lesen ist, wenn Sie eine Methode aufrufen, und es nicht offensichtlich ist, was mit dem Namen zurückgegeben wird.

var selbst verlangsamt die Dinge nicht, aber es gibt eine Einschränkung, an die nicht viele Leute denken. Wenn Sie dies tun var result = SomeMethod();, erwartet der Code danach eine Art Ergebnis, bei dem Sie verschiedene Methoden oder Eigenschaften oder was auch immer aufrufen würden. Wenn SomeMethod()die Definition in einen anderen Typ geändert wurde, aber der vom anderen Code erwartete Vertrag weiterhin erfüllt wurde, haben Sie gerade einen wirklich bösen Fehler erstellt (wenn natürlich keine Unit- / Integrationstests durchgeführt wurden).

Daniel Lorenz
quelle
0

Es hängt von der Situation ab, wenn Sie versuchen, diesen Code unten zu verwenden.

Der Ausdruck wird in "OBJECT" konvertiert und verringert die Leistung so sehr, aber es ist ein isoliertes Problem.

CODE:

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;
    }
}

Oben Ergebnisse mit 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();
}

Wenn Sie diesen Code ausführen möchten, verwenden Sie den folgenden Code und ermitteln Sie die Zeitdifferenz.

        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();
    }

Grüße

Silvio Garcez
quelle