성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>(번역글) .NET Internals Cookbook Part 8 - C# gotchas</h1> <p> 이번에도 .NET Internals Cookbook 시리즈의 8번째 글을 번역한 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Internals Cookbook Part 8 - C# gotchas ; <a target='tab' href='https://blog.adamfurmanek.pl/2019/04/06/net-internals-cookbook-part-8/'>https://blog.adamfurmanek.pl/2019/04/06/net-internals-cookbook-part-8/</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>52. class와 interface 내에 자식 class/interface를 정의할 수 있을까?</div> <br /> 해보면 알죠? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading; namespace Program { public class Program { public static void Main(string[] args) { } } } class A { class ClassInAClass { } } interface B { // 컴파일 에러 // error CS0524: 'B.ClassInAnInterface': interfaces cannot declare types class ClassInAnInterface { } } class C { interface InterfaceInAClass { } } interface D { // 컴파일 에러 // error CS0524: 'D.InterfaceInAnInterface': interfaces cannot declare types interface InterfaceInAnInterface { } } </pre> <br /> 보는 바와 같이 interface 내에는 class/interface에 상관없이 포함할 수 없습니다. 이에 대해 ECMA 표준에 제출된 문서를 언급하며 interface가 다음과 같이 정의되었다고 합니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> a named group of methods, locations, and other contracts that shall be implemented by any object type that supports the interface contract of the same name.<br /> </div><br /> <br /> 즉, interface는 실제 타입은 아니며 타입이 제공하는 메서드들에 이름 지어진 설명자에 불과하다고 합니다. 실제로 다음의 코드에서 Baz 타입은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; public class Program { public static void Main(string[] args) { IFoo foo = new Baz(); foo.Method(); } } interface IFoo { void Method(); } class Bar { public void Method() { Console.WriteLine("Bar!"); } } <span style='color: blue; font-weight: bold'>class Baz</span> : Bar, IFoo { } </pre> <br /> IFoo를 상속하지만 정작 IFoo가 정의한 메서드는 구현하지 않음에도 IFoo 인터페이스로의 형변환과 함께 동작에는 아무런 문제가 없습니다.<br /> <br /> 심지어, IFoo의 메서드를 담고 있는 Bar::Method는 위와 같이 하나의 어셈블리에 Bar/Baz가 포함되어 컴파일이 되면 Bar::Method를 virtual final 메서드로 컴파일합니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .class private auto ansi beforefieldinit Bar extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { } // IFoo를 구현하지도 않았고, Bar::Method에 virtual을 지정하지도 않았지만! .method public hidebysig newslot <span style='color: blue; font-weight: bold'>virtual final</span> instance void Method() cil managed { } } .class private auto ansi beforefieldinit Baz extends Bar implements IFoo { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { } } </pre> <br /> 반면 Bar와 Baz가 다른 어셈블리에 정의되었다면 Bar::Method는 그대로 일반 메서드로 컴파일이 되지만, Baz에는 IFoo.Method가 virtual final로 포함이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // A 어셈블리 .class public auto ansi beforefieldinit Bar extends [System.Runtime]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { } .method public hidebysig instance void Method() cil managed { } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // A 어셈블리를 참조한 B 어셈블리 .class private auto ansi beforefieldinit Baz extends [ClassLibrary1]Bar implements [ClassLibrary1]IFoo { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { } .method private hidebysig newslot virtual final instance void IFoo.Method() cil managed { .override [ClassLibrary1]IFoo::Method } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>53. switch는 어떻게 컴파일이 될까?</div> <br /> 컴파일러 버전, 변수 형식, label의 수에 따라 다릅니다. 일례로 다음의 코드를 .NET 4.5, C# 7 컴파일러로 빌드하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static int SwitchAsIfElse(int x) { switch (x) { case 1: return 1; case 2: return 2; default: return -1; } } </pre> <br /> 이렇게 if 문의 조합으로 번역이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static int SwitchAsIfElse(int x) { int result; if (x != 1) { if (x != 2) { result = -1; } else { result = 2; } } else { result = 1; } return result; } </pre> <br /> 반면 다음의 코드는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static int SwitchAsSwitch(int x) { switch (x) { case 1: return 1; case 2: return 2; case 3: return 3; case 4: return 4; case 5: return 5; case 6: return 6; default: return -1; } } </pre> <br /> 그대로 switch 구문으로 번역됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static int SwitchAsSwitch(int x) { int result; switch (x) { case 1: result = 1; break; case 2: result = 2; break; case 3: result = 3; break; case 4: result = 4; break; case 5: result = 5; break; case 6: result = 6; break; default: result = -1; break; } return result; } </pre> <br /> 문자열 상수의 경우에는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void SwitchAsIfElse(string x) { switch (x) { case "1": break; case "2": break; case "3": break; case "4": break; case "5": break; case "6": break; } } </pre> <br /> 단순 if 문의 조합으로 번역되기도 하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static void SwitchAsIfElse(string x) { if (!(x == "1")) { if (!(x == "2")) { if (!(x == "3")) { if (!(x == "4")) { if (!(x == "5")) { if (!(x == "6")) { } } } } } } } </pre> <br /> 다음과 같이 레이블이 늘어나면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void SwitchWithHashes(string x) { switch (x) { case "1": break; case "2": break; case "3": break; case "4": break; case "5": break; case "6": break; case "7": break; } } </pre> <br /> 속도를 높이기 위해 해시코드 연산이 동반됩니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static void SwitchWithHashes(string x) { uint num = <PrivateImplementationDetails>.ComputeStringHash(x); if (num <= 839689206u) { if (num != 806133968u) { if (num != 822911587u) { if (num == 839689206u) { if (!(x == "7")) { } } } else { if (!(x == "4")) { } } } else { if (!(x == "5")) { } } } else { if (num <= 873244444u) { if (num != 856466825u) { if (num == 873244444u) { if (!(x == "1")) { } } } else { if (!(x == "6")) { } } } else { if (num != 906799682u) { if (num == 923577301u) { if (!(x == "2")) { } } } else { if (!(x == "3")) { } } } } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>54. __makeref, __refvalue, __reftype, __arglist</div> <br /> 처음 3개의 예약어(__makeref, __refvalue, __reftype)에 대해서는 다음의 글을 참고하시고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Custom memory allocation in C# Part 1 - Allocating object on a stack ; <a target='tab' href='https://blog.adamfurmanek.pl/2016/04/23/custom-memory-allocation-in-c-part-1/'>https://blog.adamfurmanek.pl/2016/04/23/custom-memory-allocation-in-c-part-1/</a> </pre> <br /> __arglist 사용 예는 이렇습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading; namespace Program { public class Program { public static void Main(string[] args) { ShowArgList(<span style='color: blue; font-weight: bold'>__arglist(5, 6.0, "abc", new object())</span>); } public static void ShowArgList(<span style='color: blue; font-weight: bold'>__arglist</span>) { Console.WriteLine("ShowArgList"); <span style='color: blue; font-weight: bold'>ArgIterator ai = new ArgIterator(__arglist);</span> while (ai.<span style='color: blue; font-weight: bold'>GetRemainingCount</span>() > 0) { <a target='tab' href='http://www.sysnet.pe.kr/2/0/11529'>TypedReference</a> tr = ai.GetNextArg(); Console.WriteLine(TypedReference.ToObject(tr)); } } } } </pre> <br /> 이때 ShowArgList를 호출하는 IL 코드는 "vararg"와 "..."가 사용됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > IL_0015: call vararg void Program.Program::ShowArgList(..., int32, float64, string, object) </pre> <br /> 참고로, 저도 예전에 __makeref, __refvalue는 다음의 글에서 다룬 적이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C#에서 enum을 boxing 없이 int로 변환하기 - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11506'>http://www.sysnet.pe.kr/2/0/11506</a> (Unity가 사용하는) 모노 런타임의 __makeref 오류 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11564'>http://www.sysnet.pe.kr/2/0/11564</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>55. C/C++의 union을 C#으로 표현하는 방법</div> <br /> LayoutKind를 Explicit로 하고 FieldOffset을 같게 만드는 방법으로 union을 표현할 수 있습니다. 다음은 이에 대한 예제입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading; using System.Runtime.InteropServices; public class Program { public static void Main(string[] args) { var array = new ByteArray(); array.Int = 0xBADF00D; Console.WriteLine(array.Int); Console.WriteLine(array.Byte1.ToString("X")); Console.WriteLine(array.Byte2.ToString("X")); Console.WriteLine(array.Byte3.ToString("X")); Console.WriteLine(array.Byte4.ToString("X")); } } [StructLayout(LayoutKind.Explicit)] struct ByteArray { [FieldOffset(0)] public byte Byte1; [FieldOffset(1)] public byte Byte2; [FieldOffset(2)] public byte Byte3; [FieldOffset(3)] public byte Byte4; [FieldOffset(0)] public int Int; } /* 출력 결과 195948557 D F0 AD B */ </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>56. x == x 평가가 false로 나오는 경우와 이럴 때 x.Equals(x)로 비교한다면?</div> <br /> 다음의 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading; namespace Program { public class Program { public static void Main(string[] args) { // 컴파일 경고 // warning CS1718: Comparison made to same variable; did you mean to compare something else? Console.WriteLine(Double.NaN == Double.NaN); Console.WriteLine(Double.NaN.Equals(Double.NaN)); } } } /* 출력 결과 False True */ </pre> <br /> x == x 평가가 False로 나옵니다. 어찌 보면 이상할 수 있는데 IEEE 754 표준에 "NaN"은 자신과 비교할 수 없다고 명시되었다고 합니다. 그래서 이건 꼭 C#만의 코드 결과가 아니라 다른 언어에서도 마찬가지의 결과를 볼 수 있습니다.<br /> <br /> 그러면서도 Equals 연산자는 NaN까지도 비교할 수 있게 만들어진 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Equals 메서드와 == 연산자의 차이점 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11872#tag24'>https://www.sysnet.pe.kr/2/0/11872#tag24</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>57. C#에서 (타입에 소속되지 않은) 전역 변수를 가질 수 있을까?</div> <br /> C# 문법으로는 안 되지만, IL 코드로는 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp1 { // ...[생략]... .hash algorithm 0x00008004 .ver 1:0:0:0 } .module ConsoleApp1.exe // MVID: {DBF8C2F8-6FC2-4C37-9E70-F78CE357CCE8} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x02B90000 <span style='color: blue; font-weight: bold'>.field static int32 X</span> .class private auto ansi Program extends [mscorlib]System.Object { .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 8 (0x8) .maxstack 1 .locals init ([0] class Program pg) IL_0000: nop IL_0001: newobj instance void Program::.ctor() IL_0006: stloc.0 <span style='color: blue; font-weight: bold'>ldsfld int32 X</span> call void [mscorlib]System.Console::WriteLine(int32) IL_0007: ret } // end of method Program::Main .method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 9 (0x9) .maxstack 8 IL_0000: nop IL_0001: ldc.i4.2 <span style='color: blue; font-weight: bold'>stsfld int32 X</span> IL_0007: nop IL_0008: ret } // end of method Program::.cctor .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Program::.ctor } // end of class Program </pre> <br /> 필드를 정의하는 방법과 get/set하는 코드에 어떠한 타입도 붙지 않은 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [필드 정의] .field static int32 X [get 접근] ldsfld int32 X [set 접근] stsfld int32 X </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>58. try/catch/finally 절에서 yield/async를 할 수 있을까?</div> <br /> yield는 사용할 수 없지만 async 코드는 (C# 6.0부터) 호출할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Program { public class Program { public static void Main(string[] args) { } static IEnumerable<int> YieldWithTryCatchFinally() { yield return 1; try { // error CS1626: Cannot yield a value in the body of a try block with a catch clause // yield return 2; } catch { // error CS1631: Cannot yield a value in the body of a catch clause // yield return 3; } finally { // error CS1625: Cannot yield in the body of a finally clause // yield return 4; } } static IEnumerable<int> YieldWithTryFinally() { yield return 1; try { yield return 2; } finally { // error CS1625: Cannot yield in the body of a finally clause // yield return 3; } } static async Task AwaitWithTryCatchFinally() { await Task.Yield(); try { await Task.Yield(); } catch { await Task.Yield(); } finally { await Task.Yield(); } } } } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1442&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </h1><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1810
(왼쪽의 숫자를 입력해야 합니다.)