문자열 출력 : C #의 형식 또는 연결? 다음 중 선호하는

문자열을 출력하거나 연결한다고 가정 해 봅시다. 다음 중 선호하는 스타일은 무엇입니까?

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

오히려 형식을 사용하거나 단순히 문자열을 연결합니까? 가장 좋아하는 것은 무엇입니까? 이것들 중 하나가 당신의 눈을 아프게합니까?

하나를 사용하는 것이 아니라 다른 것을 사용하는 합리적인 논쟁이 있습니까?

나는 두 번째로 갈 것입니다.



답변

이 코드를 사용해보십시오.

약간 수정 된 코드 버전입니다.
1. Console.WriteLine은 측정하려고하는 것보다 몇 배나 느릴 수 있으므로 제거했습니다.
2. 루프 전에 스톱워치를 시작하고 바로 멈 춥니 다.이 방법으로 함수 실행에 26.4 틱이 걸리더라도 정밀도를 잃지 않습니다.
3. 결과를 일부 반복으로 나누는 방식이 잘못되었습니다. 1000 밀리 초와 100 밀리 초가 있으면 어떻게되는지 확인하십시오. 두 경우 모두 1000000으로 나눈 후 0ms가됩니다.

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

그게 내 결과입니다.

1000000 x 결과 = string.Format ( “{0} {1}”, p.FirstName, p.LastName); 소요 : 618ms-2213706 틱
1000000 x 결과 = (p.FirstName + “”+ p.LastName); 소요 : 166ms-595610 틱


답변

많은 사람들이 즉시 가장 빠른 코드를 찾고 싶어한다는 사실에 놀랐습니다. 한 번의 반복으로 처리하는 데 1 초도 걸리지 않으면 최종 사용자에게 눈에 띄게 나타날까요? 별로.

조기 최적화 = 실패.

String.Format옵션은 아키텍처 관점에서 가장 의미가 있기 때문에 옵션을 사용합니다. 나는 그것이 문제가 될 때까지 성능에 관심이 없다.

고객이 나중에 표시 여부를 구성 할 수 있도록 "Firstname Lastname"또는 "Lastname, Firstname."형식 옵션을 사용하여 이를 변경하려는 경우이를 고려하십시오 . 형식 문자열을 바꾸면됩니다. concat을 사용하면 추가 코드가 필요합니다. 이 특정 예제에서는 큰 소리처럼 들리지 않지만 외삽됩니다.


답변

오, 친애하는-다른 답글 중 하나를 읽은 후 작업 순서를 반대로 바꾸어 보았습니다. 따라서 연결을 먼저 수행 한 다음 String.Format …

Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks

따라서 작업 순서가 큰 차이를 만들거나 첫 번째 작업은 항상 훨씬 느립니다.

다음은 작업이 두 번 이상 완료된 실행 결과입니다. 주문 변경을 시도했지만 첫 번째 결과가 무시되면 일반적으로 동일한 규칙을 따릅니다.

Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks

보시다시피 동일한 메소드의 후속 실행 (코드를 3 개의 메소드로 리팩토링)이 점차 빨라졌습니다. 가장 빠른 것은 Console.WriteLine (String.Concat (…)) 메소드에 이어 정상 연결과 형식화 된 조작으로 나타납니다.

첫 번째 작업이 모든 시간을 다시 시작하기 전에 Console.Writeline ( “Start!”)을 배치하면 시작시 지연이 콘솔 스트림이 초기화 될 수 있습니다.


답변

문자열은 변경할 수 없습니다. 이는 코드에서 동일한 작은 메모리 조각이 반복해서 사용됨을 의미합니다. 동일한 두 문자열을 함께 추가하고 동일한 새 문자열을 반복해서 생성해도 메모리에는 영향을 미치지 않습니다. .Net은 동일한 메모리 참조를 사용하기에 충분합니다. 따라서 코드는 두 concat 메소드의 차이점을 실제로 테스트하지 않습니다.

크기에 대해 이것을보십시오 :

Stopwatch s = new Stopwatch();

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;

Random random = new Random(DateTime.Now.Millisecond);

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();

s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();

StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
    sb.Clear();
    sb.Append(random.Next().ToString());
    sb.Append(" ");
    sb.Append(random.Next().ToString());
    result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();

Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();

샘플 출력 :

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks

답변

불쌍한 번역가들을 동정하십시오

응용 프로그램이 영어로 유지되는 것을 알고 있다면 괜찮습니다. 시계 눈금을 저장하십시오. 그러나 많은 문화권에서는 일반적으로 주소와 같이 성 이름이 표시됩니다.

따라서 string.Format()특히 영어가 모국어가 아닌 곳으로 응용 프로그램을 사용하려는 경우을 사용하십시오.


답변

100,000 회 이상의 반복 결과는 다음과 같습니다.

Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks

벤치 코드는 다음과 같습니다.

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

for (var i = 0; i < n; i++)
{
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();
    cElapsedMilliseconds += s.ElapsedMilliseconds;
    cElapsedTicks += s.ElapsedTicks;
    s.Reset();
    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    fElapsedMilliseconds += s.ElapsedMilliseconds;
    fElapsedTicks += s.ElapsedTicks;
    s.Reset();
}

Console.Clear();

Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");

그래서 누구의 답을 답으로 표시할지 모르겠습니다. 🙂


답변

문자열을 연결하는 것은 간단한 시나리오에서 좋습니다. LastName, FirstName과 같이 복잡한 것보다 복잡합니다. 이 형식을 사용하면 코드를 읽을 때 문자열의 최종 구조가 무엇인지 알 수 있습니다. 연결하면 최종 결과를 즉시 식별하는 것이 거의 불가능합니다 (이와 같은 매우 간단한 예는 제외).

장기적으로 의미하는 것은 문자열 형식을 변경하기 위해 다시 돌아올 때 서식 문자열을 팝업하여 약간 조정하거나 눈썹에 주름을 잡고 움직일 수 있다는 것입니다. 텍스트와 혼합 된 속성 접근 자의 종류로 인해 문제가 발생할 가능성이 높습니다.

.NET 3.5를 사용하는 경우 다음 과 같은 확장 방법을 사용하여 다음 과 같이 커프 구문에서 쉽게 이동할 수 있습니다.

string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);

마지막으로 응용 프로그램이 복잡 해짐에 따라 응용 프로그램에서 문자열을 제대로 유지 관리하기 위해 문자열을 리소스 파일로 이동하여 지역화하거나 단순히 정적 도우미로 옮기려고 할 수 있습니다. 일관되게 형식을 사용하면 훨씬 쉽게 달성 할 수 있으며 코드를 간단히 리팩터링하여 다음과 같은 것을 사용할 수 있습니다

string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);