1. garbage collector 동작 방식
-
managed heap : reference type의 모든 instance가 생성되고 할당되는 memory pool이다. object가 초기화되었을 때 CLR은 managed heap에 address space를 할당한다.
-
garbage collector : 메모리를 관리해주는 mechanism.
-
garbage collection : 메모리를 해제하고 새롭게 재배치 하는 작업을 칭하고 자동으로 수행되지만 수동으로도 수행을 명령할 수도 있다. garbage collection이 수행되는 시기는 개발자가 알 수 없지만 분명한 것은 메모리가 부족하면 분명 garbage collection이 일어난다는 것이다.
-
garbage collector 동작방식 : CLR의 heap은 generation로 나뉘어 관리된다. managed heap 입장에서 generation을 구분하는 것은 단지 메모리의 위치를 가리키는 내부 포인터에 의해 이뤄진다. 처음 new로 할당된 객체는 generation 0에 속한다. generation 0객체의 총 용량이 일정 크기를 넘어가면 GC는 가비지 수집을 한다. 사용되지 않는 generation 0 객체가 있으면 없애고, 그 시점에도 사용되고 있는 객체는 generation 1로 승격한다. generation 1로 승격된 객체의 총 용량도 일정 크기를 넘어가게 되면 CG는 generation 0, 1세대에 모두 가비지 수집을 한다. 1세대의 객체가 그 시점에도 사용되고 있으면 generation 2로 승격한다. generation 2로 승격된 객체의 총 용량도 일정 크기를 넘어가게되면 GC는 generation 0, 1, 2의 모든 객체를 가비지 수집한다. CLR의 generation은 generation 2가 끝이다. 이후 generation 2의 메모리 공간은 시스템이 허용하는 한 계속 커지게 된다.
-
full garbage collector : GC가 generation을 구분한 이유는 프로그램 실행 도중 generation 0에 할당되고 수집되는 비율이 매우 높다는 통계적 근거를 기반으로 한다. 따라서 generation 0객체가 꾸준히 할당되어 가비지 수집이 될 기준을 넘어서면 GC는 모든 세대에 걸쳐 가지 수집을 하지 않고 우선 generation 0 heap에 대해서만 빠르게 수행한다. generation 0에만 가비지 수집을 하게 되면 generation 1 heap이 필연적으로 자라게된다. 그래서 generation 0 가비지 수집만으로 메모리 확보가 부족해지면 generation 1 heap까지 가비지 수집을 하게된다. 이런 현상이 계속되면 전체 generation에 가비지 수집을 하는 경우가 발생하는데 이를 Full GC라고 한다.
-
GC.Collect(int generation) : 가비지 수집을 할 수 있는 generation을 입력받는 method다. 하지만 GC.Collect method를 명시적으로 호출하는 것을 권장하지 않는데 대용량 object를 생성한 경우 강제로 가비지 수집하는 목적으로 사용되기도 한다.
-
large object heap : 가비지 수집으로 살아남은 object가 이동되는데 이런 식의 가비지 수집은 대용량 객체에게는 부담이 된다. 이 때문에 CLR은 일정 크기 이상의 객체는 별도로 LOH(Large Object Heap)라는 특별한 heap에 생성한다. LOH에 할당된 객체는 가비지 수집이 발생해도 메모리 주소가 바뀌지 않는다. 이 때문에 LOH에 object를 생성/해제하다 보면 필연적으로 메모리 fragmentation현상이 발생한다. 또 다른 특징으로는 LOH에 생성된 object는 초기부터 2세대에 할당한다. 이 때문에 Full GC가 발생하지 않는 한 LOH의 객체는 수집 과정을 거치지 않는다.
-
root reference : heap object를 참조하는 stack 변수, , 또 다른 heap object를 root reference라고 한다.
-
release resource : 아래의 코드에서 fs.Close();를 주석처리한다면, 프로그램이 실행된 상태에서 FileCreate method를 벗어나도 ouput.log파일이 삭제되지 않는다. 왜냐하면 FileStream object가 여전히 managed heap에 남아 있는 상태고 그 파일을 독점적으로 소유하고 있어 잠겨 있기 때문이다.
private static void FileCreate() { FileStream fs = new FileStream("output.log", FileMode.Create); fs.Close(); }
마이크로소프트는 자원 해제가 필요하다고 판단되는 모든 object는 개발자로 하여금 IDisposable interface를 상속받도록 권장하고 있다. 이 interface에 정의된 method는 단 하나다.
//public interface IDisposable //{ // void Dispose(); //} // FileStream object도 IDisposable을 상속받고 있으며, Dispose method를 구현하고 있다. private static void FileCreate() { FileStream fs = new FileStream("output.log", FileMode.Create); fs.Dispose(); }
-
destructor : object가 managed heap에서 제거될 때 호출되는 method로 destructor를 만드려면 class와 동일한 이름이로 ~(tilde)기호만 붙이면 된다. 개발자가 Dispose를 호출하지 않았을 때 class에 포함된 destructor로 Dispose method를 호출 할 수 있다. 즉, class를 만든 개발자가 해당 class를 사용하는 개발자의 실수를 예상하고 방어적인차원에서 사용할 수 있다. destructor 호출 시 GC의 효율을 개선하기 위해 GC.SuppressFinalize method를 호출하는 것을 권장한다.
class UnmanagedMemoryManager { ~UnmanagedMemoryManager() { Dispose(); Console.WriteLine("수행됨"); //managed heap에서 제거될 때 수행 } }
managed heap에 할당된 object의 root reference가 없어지면 언젠가는 GC의 실행으로 메모리가 반드시 해제된다. 이것은 "managed heap"인 경우에 한해서다. "unmanaged heap"에 할당되는 메모리 자원, 또는 window os와 연동되는 handle과 같은 자원은 GC의 관리를 벗어나므로 개발자가 직접 해제를 해야한다.
2. Exception 처리 (try, catch, finally)
CLR에 의해 전달되는 exception는 그 자체도 type instance다. System.NullReferenceException, System.IndexOutOfRangeException는 class의 이름이고 BCL의 하나인 mscorlib.dll 파일에 정의돼 있다.
member | type | Description |
---|---|---|
Message | property | Exception을 설명하는 message. "Index was outside the bounds of the array." |
Source | property | Exception을 발생시킨 application name. "ConsoleApplication14" |
StackTrace | property | Exception을 발생시킨 method call stack. at ConsoleApplication14.Program.Main(String[] args) in c:\users\jaehyun\documents\visual studio 2015\Projects\ConsoleApplication14\ConsoleApplication14\Program.cs:line 15 |
ToString | method | Message, StackTrace 내용을 포함하는 문자열. System.IndexOutOfRangeException: Index was outside the bounds of the array. at ConsoleApplication14.Program.Main(String[] args) in c:\users\jaehyun\documents\visual studio 2015\Projects\ConsoleApplication14\ConsoleApplication14\Program.cs:line 15 |
try
{ }
catch
{ //오직 exception이 발생한 경우에만 실행 }
finally
{ //try 블록 내에서 exception이 발생하는 것과 상관없이 언제나 실행(release resource 용도로 적합) }
// catch (System.Exception e) { }
// catch (System.NullReferenceException) {}
// exception type을 명시해서 받을 수 있지만, System.Exception이 상위에 정의되어 있으면 아래에 정의된 catch블록은 실행되지 않는다.
-
exception 발생
특이하게도 catch 블록 내에 있는 throw는 exception object없이 단독으로 사용할 수 있다.
try {}
catch (System.Exception)
{
throw; // throw ex;
}
throw와 throw ex표현에는 차이점이 있는데, throw를 단독으로 사용한 경우 예외를 발생시킨 call stack이 모두 출력되지만 throw ex를 한 경우에는 exception이 발생한 call stack은 없어지는 차이가 발생한다. 따라서 throw를 단독으로 사용해야만 오류의 원인을 좀 더 파악할 수 있다.
-
사용자 정의 exception type
exception은 type이다. 따라서 원한다면 별도로 class를 만들어 사용할 수 있다. 사용자 정의 exception은 System.Exception을 부모로 두는 것을 권장한다.using System; class InvalidPasswordException : Exception { public InvalidPasswordException(string msg) : base(msg) { } } class Program { static void Main(string[] args) { string txt = Console.ReadLine(); if (txt != "123") { InvalidPasswordException ex = new InvalidPasswordException("틀린암호입니다."); throw ex; } } }
-
올바른 예외처리
-
public method에 한해서는 parameter 값이 올바른지 확인하고, 올바른 parameter가 아니라면 exception을 발생시킨다.
-
exception을 범용적으로 catch하는 것을 thread마다 하나만 둔다. 그 외에는 catch syntax에 반드시 예외 타입을 적용한다.
-
자원수거가 목적은 try/finally의 조합은 언제든 사용할 수 있다.
-
성능상 문제가 발생할 수 있는 경우, 즉, 호출 시 예외가 대량으로 발생하는 method가 있다면 exception처리가 없는 method를 함께 제공한다.
마이크로소프트는 Parse method를 out parameter를 사용해 개선한 TryParse method를 .NET 2.0의 BCL에 포함시켰다. 이 method는 문자열을 숫자로 바꿀 수 있는 경우에만 out 형식의 인자에 숫자값을 담고, method 실행 성공 여부만 반환하고 exception을 발생시키지 않는다.try/catch는 thread 단위마다 단 한번만 전역적으로 적용해야 한다.그 밖의 코드에서 예외 처리가 필요하다면 try/catch를 하더라도 catch에 정확한 예외 타입을 지정하는 것을 원칙으로 한다.
3. DateTime, TimeSpan, StopWatch
● System.DateTime
- UTC(Universal Time, Coordinated) : 지구의 자전으로 인해 시간차가 발생하는 지역은 Time Zone을 두어 상대적인 차이를 조정하는데 그리니치 천문대가 위치한 영국의 동쪽에 위치하고 날짜 분기선 이전에 있는 대한민국은 Time Zone이 UTC +9에 해당한다. 이 때문에 UTC +9를 한국 표준시(KST:Korea Standard Time)라고도 한다.
- local time : Time Zone이 반영된 시간.
- Epoch Time, Unix TimeStmp, POSIX Time : 유닉스 및 자바 관련 플랫폼에서는 1970년 1월 1일을 기준으로하는 시간(닷넷의 DateTime은시간의 기준값이 1년 1월 1일) 자바에서는 System.currentTimeMillis method를 제공하는데, 이 method는 현재 시간을 UTC 기준의 밀리초 단위로 Epoch 시간 이후로 흐른 값을 반환한다.
영국은 UTC와 local time이 동일하지만 시간대가 UTC +9인 대한민국은 영국이 한밤 중일 때 지역 시간은 오전 9시가 된다. 따라서 영국을 제외하고는 거의 모든 나라에서 시간을 나타낼 때 UTC인지 local time인지를 명시해야만 정확한 시간을 알 수 있다. .Net의 DateTime type은 이 구분을 enum type인 Kind속성으로 알려준다.
열거형 값 | 설명 |
---|---|
Unspecified | 어떤 형식인지 지정되지 않은 시간 |
Utc | 동시간의 그리니치 천문대 시간 |
Local | 시간대를 반영한 지역시간 |
* **문제) Unix Time을 DateTime으로 바꾸기 / DateTime을 UnixTime으로 바꾸기** ``` cs static void Main(string[] args) { DateTime unixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
//DateTime을 UnixTime으로 바꾸기
long unixMillis = (DateTime.UtcNow.Ticks - unixStartTime.Ticks) / 10000;
//Unix Time을 DateTime으로 바꾸기
long dateMillis = unixMillis + (unixStartTime.Ticks / 10000);
DateTime dateTime = new DateTime(dateMillis * 10000, DateTimeKind.Utc);
Console.WriteLine(dateTime.ToLocalTime());
}
* **검증**
``` c
#include <stdio.h>
#include <time.h>
int main(int argc, char* argv[])
{
time_t current_time;
time(¤t_time);
printf("%ld\n", current_time);
printf(ctime(¤t_time));
return 0;
}
linux환경에서 위와 같은 main.c를 작성하고 실행시키면 다음과 같은 결괏값이 나온다.
구해지는 시간(1448967159)은 1970년 1월 1일 0시부터 함수를 호출할 때 까지의 초단위이므로 milisecond값인 1448967159000값을 입력값으로 윈도우환경에서 코드를 실행하면(
long unixMillis = 1448967159000;
) 아래와 같은 결괏값이 나온다.
● System.TimeSpan
DateTime type은 유일하게 빼기 연산이 된다. 빼기의 결괏값은 TimeSpan으로 나온다. TimeSpan의 TotalDays 등의 속성을 통해 단위의 시간 간격을 알 수 있다.
DateTime endOfYear = new DateTime(DateTime.Now.Year, 12, 31);
DateTime now = DateTime.UtcNow;
TimeSpan gap = endOfYear - now;
Console.WriteLine("올해의 남은 날짜 : " + gap.TotalDays);
#### **● System.StopWatch** TimeSpan보다 더 정확한 시간차 계산을 위해 Stopwatch type을 제공한다. 주로 특정 코드구간에 대한 성능을 측정할 때 자주 사용된다. ``` cs Stopwatch st = new Stopwatch(); st.Start(); Sum(); st.Stop(); Console.WriteLine("Second : ", st.ElapsedTicks / 10000 / 1000); ``` _ _ _ ### **4. String 대표 method 정리**
member | type | description |
---|---|---|
Format | method | 형식에 맞는 문자열을 생성해 반환 |
IndexOf | method | 문자 또는 문자열을 포함하는 경우 그 위치를 반환하고 없으면 -1 반환 |
Replace | method | 첫 번째 인자의 문자 또는 문자열을 두 번째 인자의 값으로 치환된 문자열을 반환 |
Split | method | 주어진 문자 또는 문자열 구분자로 나뉜 문자열 배열을 반환 |
Substring | method | 시작과 길이에 해당하는 만큼의 문자열을 반환 |
TrimXXX | method | 문자열의 앞뒤에 주어진 문자가 있는 경우 삭제한 문자열을 반환 문자가 지정되지 않으면 공백문자를 제거해서 반환 |
Length | method | 문자의 길이를 integer로 반환 |
-
System.Text.StringBuilder
string type은 immutable object이기 때문에 string에 대한 모든 변환은 새로운 메모리 할당을 발생시킨다. 이러한 문제를 해결하기 위해 BCL에 추가된 클래스가 StringBuilder다. StringBuilder는 Append method를 제공한다. 내부적으로 일정한 양의 메모리를 미리 할당하고 Append method에 들어온 인자를 복사하는 방식이다. 문자열을 연결하는 작업이 많을 때는 StringBuilder를 사용하는 것을 권장한다.
5. Encoding 정리
string textData = "한국";
// String은 바이트로 직접 변환할 수 없으며, Encoding을 통해 변환 가능. 16바이트 생성
byte[] buf = Encoding.Unicode.GetBytes(textData);
Console.WriteLine(Encoding.Unicode.GetString(buf));
정적속성 | 설명 |
---|---|
ASCII | 7bit ASCII 문자셋을 위한 인코딩 |
Dafault | 시스템 기본 문자셋을 위한 인코딩 한글 윈도우의 경우 ks_c_5601-1987, 영문 윈도우의 경우 iso_8859-1 |
Unicode | 유니코드 문자셋의 UTF-16 인코딩 |
UTF32 | 유니코드 문자셋의 UTF-32 인코딩 |
UTF8 | 유니코드 문자셋의 UTF-8 인코딩 |
6. BitConverter 정리
문자열은 인코딩 방식에 따라 같은 문자열이라도 바이트 배열로의 변환이 달라질 수 있다.하지만 그 밖의 기본 타입(byte, short, int 등)은 변환 방법이 고정돼 있다.
byte[] boolBytes = BitConverter.GetBytes(true);
byte[] shortBytes = BitConverter.GetBytes((short)32000);
bool boolResult = BitConverter.ToBoolean(boolBytes, 0);
bool shortResult = BitConverter.ToBoolean(shortBytes, 0);
Console.WriteLine(BitConverter.ToString(boolBytes));
Console.WriteLine(BitConverter.ToString(shortBytes));
7. FileStream 정리
System.IO.StreamWriter / System.IO.StreamReader Stream에 문자열을 쓰려면 반드시 Encoding type을 이용해 byte array로 변환해야한다. 문자열을 쓸 때마다 매번 이 같은 식으로 변환을 거쳐야 하는 것은 번거로우므로 마이크로소프트에서는 문자열 데이터를 Stream에 쉽게 쓸 수 있는 용도로 StreamWriter type을 BCL에 포함시켰다. StreamWriter type은 construct로 stream과 string encoding 방식을 받는다. 이후 Write 계열의 method가 호출되면 인자로 입력된 문자열을 인코딩 방식에 따라 자동으로 byte array로 변환한 후 Stream에 쓴다.
파일을 다루기 위한 BCL의 가장 기본적인 type. Stream type을 상속받았고 디스크의 파일을 대상으로 read/write 작업을 한다.
8. Thread 관련 내용 이해 및 정리
1. thread : cpu의 가장 작은 실행 단위로 Thread class를 초기화 할 때, parameter를 전달하지 않는 ThreadStart delegate와 parameter를 직접 전달하는 ParameterizedThread delegate를 사용할 수 있다.
public delegate void ThreadStart();
public delegate void ParameterizedThreadStart(object obj);
ThreadStart는 parameter를 직접 전달 받지 않는다. ParameterizedThreadStart는 하나의 parameter를 전달하고 리턴값이 없는 형식이다. Parameter의 전달은 Thread.Start() method를 호출할 때 Parameter를 전달한다.
2. race condition
race condition은 두개 또는 그 이상의 thread가 shared data에 동시에 접근해서 data를 변경할 때 발생한다.
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
race condition이 발생하는 것을 막으려면 shared data에 대한 동기화 처리를 해줘야 한다. 동기화를 처리하는 데는 여러 가지 방법 중에 shared data에 오직 한 개의 thread만 접근 할 수 있게 만드는 class가 Monitor이다.
3. System.Threading.Monitor
List<Task> tasks = new List<Task>();
Random rnd = new Random();
long total = 0;
int n = 0;
for (int taskCtr = 0; taskCtr < 10; taskCtr++)
{
tasks.Add(Task.Run(() => {
int[] values = new int[10000];
int taskTotal = 0;
int taskN = 0;
int ctr = 0;
try
{
Monitor.Enter(rnd);
// Generate 10,000 random integers
for (ctr = 0; ctr < 10000; ctr++)
values[ctr] = rnd.Next(0, 1001);
}
finally
{
Monitor.Exit(rnd);
}
taskN = ctr;
foreach (var value in values)
taskTotal += value;
Console.WriteLine("Mean for task {0,2}: {1:N2} (N={2:N0})",
Task.CurrentId, (taskTotal * 1.0) / taskN,
taskN);
Interlocked.Add(ref n, taskN);
Interlocked.Add(ref total, taskTotal);
}));
}
Task.WaitAll(tasks.ToArray());
C#에서는 try/finally + Monitor.Enter/Exit code와 동일한 역할을하는 lock keyword를 제공한다. lock syntax의 블록에 있는 모든 연산은논리적으로 원자적연산에속한다. 그리고 몇몇 단순한 유형의 연산에 대해서는 Interlocked type으로 대체할 수 있다.
lock (rnd)
{
// Generate 10,000 random integers
for (ctr = 0; ctr < 10000; ctr++)
values[ctr] = rnd.Next(0, 1001);
}
**4. thread의 동작방식** * **상시실행** thread가 생성되면 오랜 시간동안 생성돼 있는 유형.
-
1회성의 임시 실행
특정 연산만을 수행하고 바로 종료하는 유형.
2번 유형 때문에 매번 thread를 생성하는 것은 불편하다. 임시적인 목적으로 언제든 원하는 때에 thread를 사용할 수 있다면 좋을 것이다.
5. CLR thread pool
System.Threading.ThreadPool : thread pool은 background task를 수행하는 thread의 집합이다. 필요할 때마다 thread를 꺼내 쓰고 필요없어지면 다시 pool에 thread가 반환된다.
6. System.Threading.EventWaitHandle
thread로 하여금 이벤트를 기다리게 만들 수 있고, 다른 thread에서 원하는 이벤트를 발생시키는 시나리오에 적합하다. EventWaitHandel 객체를 응용하면 ThreadPool의 단점을 보완할 수 있다. EventWaitHandle.Set method를호출해 Signal 상태로 전환된 이벤트가 Non-Signal 상태로 자동으로 전환되느냐에 따라 ManualResetEvent, AutoResetEvent로 나뉜다. ManualResetEvent는 여러 개의 thread가 동시에 여러 개의 작업을 처리할 때 유용하다.
using System;
using System.Threading;
namespace MultiThrdApp
{
class Program
{
// ManualResetEvent 객체 필드
static ManualResetEvent manualEvent = new ManualResetEvent(false);
static void Main()
{
// 10개의 쓰레드 생성
// 10개 쓰레드 모두 manualEvent.WaitOne(); 에서
// 실행 중지후 대기중
for (int i = 0; i < 10; i++)
{
new Thread(Run).Start(i);
}
// 메인쓰레드
Thread.Sleep(3000);
// ManualResetEvent 객체 Set() 호출
// 10개 쓰레드 모두 실행 계속함.
manualEvent.Set();
}
static void Run(object id)
{
Console.WriteLine("{0} in Wait", id);
// ManualResetEvent 신호 대기
manualEvent.WaitOne();
Console.WriteLine("{0}: Done", id);
}
9. Directory 및 Path 관련 method 정리
- System.IO.Directory
method | 설명 |
---|---|
CreateDirectory(String) | directory를 생성한다. 이미 directory가 존재한다면 아무런 작업도 하지 않는다 |
Delete(String) | 지정된 경로에서 빈 directory만 삭제, subdirectory가 존재할 경우 IOException발생 |
Delete(String, Boolean) | Boolean이 true이면 지정된 경로에서 directory 및 subdirectory, file 삭제 |
Exists(String) | directory가 존재하는지 true/false로 반환한다 |
GetDirectories(String) | 지정된 경로의 subdirectory목록을 문자열 배열로 반환한다 |
GetDirectories(String, String) | 지정된 경로에서 지정된 pattern을 가진 subdirectory목록을 문자열 배열로 반환한다 |
GetFiles(String) | 지정된 경로에 있는 파일을 문자열 배열로 반환한다 |
GetLogicalDrives() | 시스템에 설치된 디스크의 드라이브 문자 목록을 string 배열로 반환한다 |
Move(String, String) | 디렉토리를 이동한다 |
- System.IO.Path
method | 설명 |
---|---|
ChangeExtension(String, String) | 첫 번째 인자로 전달된 경로에서 확장자 부분을 두 번재 인자로 전달된 문자열로 바꿔준다 |
Combine(String, String) | 전달된 문자열 인자를 모두 합쳐서 하나의 경로로 만든다 |
GetDirectoryName(String) | 전달된 문자열에서 파일 이름이 포함된 경우 그 파일의 부모 디렉터리 이름을 반환한다. 반면 디렉터리 이름이 포함된 경우 그 부모 디렉터리 이름을 반환한다 |
GetExtension(String) | 전달된 문자열의 확장자를 반환한다 |
GetFileName(String) | 전달된 문자열의 파일명을 반환한다 |
ObservableCollection<T>
10. 닷넷 3.0에서 System.Collections.ObjectModel namespace에 ObservableCollection라는 collection이 새롭게 추가되었다. 이 collection의 장점은 UI가 업데이트 되었을 경우 그 값이 변경되었다는 알람을 받을 수 있다는 것이다.
data binding을 할 때, ListBox, ListView 또는 TreeView 등의 ItemsControl을 사용해서 collection에 저장된 값을 표현한다. IEnumerable interface를 구현하면 어떤 collection이라도 열거할 수 있다.
그러나 collection insertion / deletion이 발생할 때, UI를 자동으로 업데이트되도록 dynamic binding을 설정하려면 INotifyCollectionChanged interface을 구현해야 한다. 이 interface는 내부 collection이 변경될 때마다 발생해야 하는 CollectionChanged event를 발생시킨다.
WPF에서는 INotifyCollectionChanged interface를 구현하는 data collection에 대한 기본 제공 구현인 ObservableCollection 클래스를 제공한다. ObservableCollection은 IList<T>
를 상속받았고 INotifyCollectionChanged interface를 구현하고 있다.
Note & Tips
#A garbage collector 동작 방식
static void Main(string[] args)
{
object a = new object();
object b = new object();
object c = new object();
DoMethod();
GC.Collect();
}
private static void DoMethod()
{
object d = new object();
object e = new object();
object f = new object();
object g = new object();
d = null;
e = null;
GC.Collect();
object h = new object();
object i = new object();
object j = new object();
object k = new object();
j = null;
k = null;
GC.Collect();
}







#B using keyword
FileLogger class에 IDisposable interface가 구현돼 있다면 FileLogger instance를 Dispose하기전에 exception이 발생할 수 있으므로 다음과 같이 구현한다.
//static void Main(string[] args)
//{
// FileLogger log = new FileLogger("sample.log");
// log.Write("Start");
// log.Dispose();
//}
static void Main(string[] args)
{
FileLogger null;
try
{
log = new FileLogger("sample.log");
log.Write("Start");
}
finally
{
log.Dispose();
}
}
c#은 try/finally를 대신하는 using keyword를 제공한다.
// 블록이 끝나는 시점에 Dispose method 자동 호출
using (FileLogger log = new FileLogger("sample.log"))
{
log.Write("Start");
}
#C String 대표 method example
// String.Format
string strFormat = String.Format("At {0}, the temperature is {1}°C.", DateTime.Now, 20.4);
Console.WriteLine(strFormat);
// String.IndexOf
string strIndexOf = "Now is the time for all good men to come to the aid of their party.";
for (int i = 0; i < strIndexOf.Length; i++)
{
int isIndex = strIndexOf.IndexOf('t', i);
if (isIndex == -1)
break;
Console.Write("{0} ", isIndex);
i = isIndex;
}
Console.WriteLine();
// String.Replace
String strReplace = "aaa";
strReplace = strReplace.Replace("a", "b").Replace("b", "c").Replace("c", "d");
Console.WriteLine("The final string: '{0}'", strReplace);
// String.Split
String strSplit = "Hello World!";
string[] arrString = strSplit.Split('o');
foreach(var elem in arrString)
{
Console.WriteLine(elem);
}
// String.Substring
String[] pairs = { "Color1=red", "Color2=green", "Color3=blue",
"Title=Code Repository" };
foreach (var pair in pairs)
{
int position = pair.IndexOf("=");
if (position < 0)
continue;
Console.WriteLine("Key: {0}, Value: '{1}'", pair.Substring(0, position), pair.Substring(position + 1));
}
// String.Trim
String strTrim = " * Hello World! ";
char[] charsToTrim = { 'H', '!' };
Console.WriteLine(strTrim.Trim(charsToTrim));
Console.WriteLine(strTrim.Trim().Trim('*').Trim().Trim(charsToTrim));
// String.Length
Console.WriteLine(strTrim.Length);