'var'을 사용하면 성능에 영향을 줍니까?


230

이전에는 왜 많은 예제가 var키워드를 사용하는지 알 수 있었는데 익명 유형에만 필요하지만 그럼에도 불구하고 'Quicker'/ eaerier 및 'just reason'코드를 작성하는 데 사용된다는 대답을 얻었습니다.

다음은 이 링크 ( "C # 3.0 - 바르하지 Objec") I 톱 var일리노이에서 올바른 형식으로 아래로 컴파일됩니다 (당신은 기사 아래 중간에 대해 그것을 볼 것).

내 질문은 var키워드를 사용하는 IL 코드가 얼마나 많이 사용하는지 , 그리고 코드가 어디서나 사용된다면 코드의 성능에 대해 측정 가능한 수준에 가깝습니까?


1
이전에 질문에 대답했지만 var에 대해 하나 더 추가하고 싶었습니다. 컴파일 시간에 해결되었지만 Visual Studio의 "모든 참조 찾기"와 Resharper의 "사용 찾기"에서 유형의 모든 사용법을 찾으려면 올바르게 발견되지 않았습니다. -너무 느려서 고칠 수는 없습니다.
KolA

@KolA 변수로 선언 된 변수는 varVisual Studio 2019에서 "모든 참조 찾기" 와 함께 가장 확실하게 작동하므로 깨졌을 경우 수정되었습니다. 그러나 Visual Studio 2012와는 거리가 멀다는 것을 확인할 수 있으므로 왜 작동하지 않는다고 주장했는지 잘 모르겠습니다.
Herohtar

@Herohtar 다음 코드 "class X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}"를 시도하고 "var x = GetX ( ) "비트는 강조 표시되지 않았습니다. 최신 VS2019에서는 현재 이것이 의미하는 바입니다. var 대신에 "X x = GetX ()"를 사용하면 강조 표시됩니다.
KolA

1
@KolA 아, 무슨 뜻인지 알겠습니다 .에서 "모든 참조 찾기"를 사용할 때 var참조로 간주되지 않습니다 . 당신이 "모든 참조 찾기"를 사용하는 경우 흥미롭게도, 그 문을, 그것은 것입니다 당신에 대한 참조를 보여 (이 여전히 나열하지 않지만 문을). 또한 커서가 켜져 있으면 동일한 문서 의 모든 인스턴스가 강조 표시됩니다 (또는 그 반대). XXvarXvarvarX
Herohtar

답변:


316

var키워드에 대한 추가 IL 코드가 없습니다 . 결과 IL은 익명이 아닌 유형에 대해 동일해야합니다. 컴파일러에서 사용하려는 유형을 파악할 수 없어서 IL을 작성할 수없는 경우 컴파일러 오류가 발생합니다.

유일한 트릭은 var유형을 수동으로 설정하려는 경우 인터페이스 또는 상위 유형을 선택한 정확한 유형을 유추하는 것입니다.


8 년 후 업데이트

이해가 변경됨에 따라 이것을 업데이트해야합니다. var메서드가 인터페이스를 반환하는 상황에서 성능에 영향을 줄 수 있다고 생각 하지만 정확한 유형을 사용했을 것입니다. 예를 들어,이 방법이 있다면 :

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

메소드를 호출하려면 다음 세 줄의 코드를 고려하십시오.

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

세 가지 모두 예상대로 컴파일 및 실행됩니다. 그러나 처음 두 줄은 정확히 같지 않으며 세 번째 줄은 첫 번째 줄이 아니라 두 번째 줄과 일치합니다. 의 서명은 Foo()을 반환하는 IList<int>것이므로 컴파일러가 bar3변수를 작성 하는 방법 입니다.

성능 관점에서는 대부분 눈치 채지 못할 것입니다. 그러나 세 번째 라인의 성능이 첫 번째 라인의 성능만큼 빠르지 않은 상황이 있습니다 . bar3변수 를 계속 사용하면 컴파일러가 같은 방식으로 메서드 호출을 전달하지 못할 수 있습니다.

지터가이 차이를 지울 수는 있지만 가능하지는 않습니다. 일반적으로 var성능 측면에서 여전히 비 인수자로 간주해야 합니다. dynamic변수를 사용하는 것과는 전혀 다릅니다. 그러나 결코 변화를 일으키지 않는다고 말하면 과장된 것일 수 있습니다.


23
뿐만 아니라 IL는 동일해야합니다 - 그것은 이다 동일합니다. var i = 42; int i = 42와 정확히 동일한 코드로 컴파일합니다.
Brian Rasmussen

15
@BrianRasmussen : 귀하의 게시물이 오래되었다는 것을 알고 있지만 (infers var i = 42;type은 int)는 같지 않다고 가정 합니다 long i = 42;. 따라서 경우에 따라 형식 유추에 대해 잘못된 가정을 할 수도 있습니다. 값이 맞지 않으면 찾기 어려운 / 가장자리 런타임 오류가 발생할 수 있습니다. 따라서 값에 명시 적 유형이없는 경우 명시 적으로 나타내는 것이 좋습니다. 예를 들어, var x = new List<List<Dictionary<int, string>()>()>()수용 가능하지만 var x = 42다소 모호하며로 작성해야합니다 int x = 42. 그러나 각자 자신에게 ...
Nelson Rothermel

50
@NelsonRothermel : 모호 var x = 42; 하지 않습니다 . 정수 리터럴은 유형 int입니다. 당신이 리터럴을 원한다면 var x = 42L;.
Brian Rasmussen

6
음, C #에서 IL은 무엇을 의미합니까? 나는 그것을 전혀 들어 본 적이 없다.
puretppc

15
다르게 행동하는 3 줄의 코드 예제에서 첫 번째 줄 은 컴파일되지 않습니다 . 두 번째와 세 번째 라인, 어떻게 , 컴파일 정확히 같은 일을. 를 Foo반환 List하지 않고을 반환하면 IList세 줄이 모두 컴파일되지만 세 번째 줄 두 번째가 아닌 첫 번째 줄처럼 동작합니다 .
Servy

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
그것은 주관적인 관점 일 뿐이며 성과에 관한 질문에 대한 대답은 아닙니다. 정답은 성능에 영향을 미치지 않는다는 것입니다. 클로즈 투표
Anders

이것은 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는 어쨌든 유형을 계산해야합니다. 컴파일러는 일치하지 않는 유형을 잡아서 오류를 던질 것이므로 실제로 비용이 들지 않습니다.
Jimmy

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를 거의 사용하지 않으며 객체를 더 빨리 선언하기 위해 intellisense에 의존합니다.

예:

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

그러나 메소드에서 객체를 반환하기 위해 var를 사용하여 코드를 더 빨리 작성합니다.

예:

var coolObject = GetCoolObject(param1, param2);

학생들을 위해 글을 쓰고 있다면 자신의 개밥을 먹고 항상 같은 "올바른"방식으로 일관되게 쓰십시오. 학생들은 종종 말과 마음으로 100 % 물건을 가져가며 길을 따라 습한 습관을 사용합니다. $ .02
ChrisH

1

"var"은 사람들이 좋아하거나 싫어하는 지역 중 하나입니다. 지역과 달리 익명 클래스를 만들 때는 var가 반드시 필요합니다.

나에게 var는 다음과 같이 직접 객체를 업데이트 할 때 의미가 있습니다.

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

즉, 당신은 쉽게 할 수 있습니다 :

Dictionary<string, string> dict = 새롭고 지능이 당신을 위해 나머지를 채울 것입니다.

특정 인터페이스로만 작업하려는 경우 호출하는 메서드가 인터페이스를 직접 반환하지 않으면 var를 사용할 수 없습니다.

Resharper는 "var"을 사용하는 편에있는 것 같습니다. 이렇게하면 더 많은 사람들이 그렇게 할 수 있습니다. 그러나 메소드를 호출하는 경우 읽기가 어렵고 이름으로 무엇이 반환되는지 명확하지 않습니다.

var 자체는 속도를 늦추지는 않지만 많은 사람들이 생각하지 않는 한 가지주의 사항이 있습니다. 그렇다면 var result = SomeMethod();코드가 다양한 메소드 또는 속성 또는 무엇이든 호출 할 수있는 일종의 결과를 기대합니다. 경우 SomeMethod()다른 유형의 정의를 변경하지만 여전히 다른 코드가 기다리고 있었다 계약을 만나, 당신은 단지 (NO 단위 / 통합 테스트의 경우, 물론) 정말 불쾌한 버그를 만들었습니다.


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.