더하기 기호를 사용할 때 얼마나 많은 String 객체가 생성됩니까?


115

아래 코드에서 더하기 기호를 사용하면 몇 개의 String 객체가 생성됩니까?

String result = "1" + "2" + "3" + "4";

다음과 같으면 "1", "2", "12"의 세 가지 String 개체를 말했을 것입니다.

String result = "1" + "2";

또한 성능 향상을 위해 String 개체가 String Intern Pool / Table에 캐시된다는 것을 알고 있지만 그게 문제가 아닙니다.


String.Intern을 명시 적으로 호출하는 경우에만 문자열이 인턴됩니다.
Joe White

7
@JoeWhite : 그렇습니까?
Igor Korkhov 2012

13
좀 빠지는. 모든 문자열 리터럴은 자동으로 인턴됩니다. 문자열 연산의 결과는 그렇지 않습니다.
Stefan Paul Noack 2012

또한 OP 예제에서는 문자열 상수가 하나만 있으며 인턴됩니다. 설명을 위해 내 답변을 업데이트하겠습니다.
Chris Shain 2012

+1. 해당 스타일로 문자열 catenation을 코딩해야하는 실제 사례의 경우 msdn.microsoft.com/en-us/library/… 의 예제 섹션에 컴파일러가 최적화 할 수없는 경우 불가능한 항목이 있습니다. 속성 매개 변수에 할당 된 값에 대한 제약으로 인해 단일 상수에.
ClickRick

답변:


161

놀랍게도 상황에 따라 다릅니다.

메소드에서이 작업을 수행하는 경우 :

void Foo() {
    String one = "1";
    String two = "2";
    String result = one + two + "34";
    Console.Out.WriteLine(result);
}

그런 다음 컴파일러는 String.Concat@Joachim이 대답 한대로 코드를 내 보냅니다 (+1은 btw).

상수 로 정의하는 경우 , 예 :

const String one = "1";
const String two = "2";
const String result = one + two + "34";

또는 원래 질문에서 와 같이 리터럴 로 :

String result = "1" + "2" + "3" + "4";

그러면 컴파일러는 이러한 +신호를 최적화 합니다. 다음과 동일합니다.

const String result = "1234";

또한 컴파일러는 불필요한 상수 식을 제거하고 사용되거나 노출 된 경우에만 내 보냅니다. 예를 들어,이 프로그램은 :

const String one = "1";
const String two = "1";
const String result = one + two + "34";

public static void main(string[] args) {
    Console.Out.WriteLine(result);
}

문자열 하나만 생성합니다. 상수 result( "1234"와 같음). one그리고 two그 결과 IL에 표시되지 않습니다.

런타임에 추가 최적화가있을 수 있습니다. 나는 IL이 생산 되는대로 가고 있습니다.

마지막으로 인턴과 관련하여 상수와 리터럴이 인턴되지만 인턴 된 값은 리터럴이 아닌 IL의 결과 상수 값입니다. 즉, 여러 개의 동일하게 정의 된 상수 또는 리터럴이 실제로 동일한 개체가되기 때문에 예상보다 더 적은 수의 문자열 개체를 얻을 수 있습니다! 이것은 다음과 같이 설명됩니다.

public class Program
{
    private const String one = "1";
    private const String two = "2";
    private const String RESULT = one + two + "34";

    static String MakeIt()
    {
        return "1" + "2" + "3" + "4";
    }   

    static void Main(string[] args)
    {
        string result = "1" + "2" + "34";

        // Prints "True"
        Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt()));

        // Prints "True" also
        Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT));
        Console.ReadKey();
    }
}

문자열이 루프에서 (또는 그렇지 않으면 동적으로) 연결되는 경우 연결 당 하나의 추가 문자열로 끝납니다. 예를 들어 다음은 12 개의 문자열 인스턴스를 만듭니다. 상수 2 개 + 반복 10 개로 각각 새 String 인스턴스가 생성됩니다.

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a";
        Console.ReadKey();
    }
}

그러나 (또한 놀랍게도) 여러 연속 연결이 컴파일러에 의해 단일 다중 문자열 연결로 결합됩니다. 예를 들어,이 프로그램은 12 개의 문자열 인스턴스 만 생성합니다! 이는 " 하나의 문에 여러 + 연산자를 사용하더라도 문자열 내용이 한 번만 복사 되기 때문 입니다. "

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a" + result;
        Console.ReadKey();
    }
}

문자열 결과 = "1"+ "2"+ 3 + 4는 어떻습니까? 여기서 2와 3은 string three = "3"처럼 선언됩니다. 문자열 4 = "4";?
The Light

그것조차도 하나의 문자열이됩니다. 방금 LinqPad를 통해 실행하여 자신을 다시 확인했습니다.
Chris Shain 2012

1
@Servy-댓글이 업데이트 된 것 같습니다. 주석을 변경할 때 변경된 것으로 표시되지 않습니다.
보안 하운드

1
완전성을 고려하는 것이 좋은 한 가지 경우는 루프에서 연결하는 것입니다. 예 : 다음 코드는 얼마나 많은 문자열 객체를 할당합니까?string s = ""; for (int i = 0; i < n; i++) s += "a";
Joren

1
LINQPad ( linqpad.net ) 또는 Reflector ( reflector.net )를 사용합니다. 전자는 임의의 코드 스 니펫의 IL을 보여주고, 후자는 어셈블리를 IL로 디 컴파일하고 해당 IL에서 동등한 C #을 다시 생성 할 수 있습니다. ILDASM ( msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx ) 이라는 내장 도구도 있습니다. IL을 이해하는 것은 까다로운 일입니다. codebetter.com/raymondlewallen/2005/를
Chris Shain

85

Chris Shain의 대답은 매우 좋습니다. 문자열 연결 최적화 프로그램을 작성한 사람으로서 두 가지 흥미로운 점을 추가 할 것입니다.

첫 번째는 연결 최적화 프로그램이 안전하게 수행 할 수있을 때 기본적으로 괄호와 왼쪽 연관성을 모두 무시한다는 것입니다. 문자열을 반환하는 M () 메서드가 있다고 가정합니다. 당신이 말하는 경우:

string s = M() + "A" + "B";

그런 다음 컴파일러는 더하기 연산자가 연관성으로 남아 있기 때문에 다음과 같습니다.

string s = ((M() + "A") + "B");

하지만 이것은:

string s = "C" + "D" + M();

와 같다

string s = (("C" + "D") + M());

그래서 그것은 상수 문자열 "CD"M().

사실, 연결 최적화는 문자열 연결이 실현 연관 및 생성 String.Concat(M(), "AB")이 왼쪽 연관성을 위반하더라도, 최초의 예를 들어.

다음과 같이 할 수도 있습니다.

string s = (M() + "E") + ("F" + M()));

그리고 우리는 여전히 String.Concat(M(), "EF", M()).

두 번째 흥미로운 점은 null 및 빈 문자열이 최적화된다는 것입니다. 따라서 이렇게하면 :

string s = (M() + "") + (null + M());

당신은 얻을 것이다 String.Concat(M(), M())

흥미로운 질문이 제기됩니다. 이건 어떨까요?

string s = M() + null;

우리는이를 최적화 할 수 없습니다.

string s = M();

때문에 M()null을 반환 할 수 있지만 null String.Concat(M(), null)을 반환하면 빈 문자열을 반환M() 입니다. 그래서 우리가하는 일은

string s = M() + null;

string s = M() ?? "";

따라서 문자열 연결이 실제로 전혀 호출 할 필요가 없음을 보여줍니다 String.Concat.

이 주제에 대한 자세한 내용은 다음을 참조하십시오.

String.Concat이 StringBuilder.Append에 최적화되지 않은 이유는 무엇입니까?


나는 거기에 몇 가지 오류가있을 수 있다고 생각합니다. 확실히, ("C" + "D") + M())생성 String.Concat("CD", M()), 없습니다 String.Concat(M(), "AB"). 그리고 더 아래, (M() + "E") + (null + M())생성해야 String.Concat(M(), "E", M())하지 String.Concat(M(), M()).
hammar

21
시작 단락에 +1. :) 이와 같은 답변은 Stack Overflow에 대해 항상 저를 놀라게하는 것입니다.
brichins

23

MSDN에서 답을 찾았습니다. 하나.

방법 : 여러 문자열 연결 (C # 프로그래밍 가이드)

연결은 한 문자열을 다른 문자열 끝에 추가하는 프로세스입니다. + 연산자를 사용하여 문자열 리터럴 또는 문자열 상수를 연결하면 컴파일러에서 단일 문자열을 만듭니다. 런타임 연결이 발생하지 않습니다. 그러나 문자열 변수는 런타임에만 연결할 수 있습니다. 이 경우 다양한 접근 방식이 성능에 미치는 영향을 이해해야합니다.


22

딱 하나만. C # 컴파일러는 문자열 상수를 접으므로 기본적으로 다음과 같이 컴파일됩니다.

String result = "1234";

""를 사용할 때마다 String 개체가 생성된다고 생각했습니다.
The Light

1
일반적으로 @William 예. 그러나 상수 계산은 불필요한 중간 단계를 제거합니다
JaredPar

13

나는 이것이 어떤 표준이나 사양에 의해 의무화되어 있는지 의심합니다. 한 버전은 다른 버전과 다른 작업을 수행 할 수 있습니다.


3
적어도 VS 2008 및 2010 용 Microsoft의 C # 컴파일러에 대해 문서화 된 동작입니다 (@ David-Stratton의 답변 참조). 즉, 당신이 옳습니다. 제가 빠른 열람에서 알 수있는 한, C # 사양은 이것을 지정하지 않으며 아마도 구현 세부 사항으로 간주되어야합니다.
Chris Shain 2012

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.