Tag Archives: C

Unity C# – IDisposable 알아보기

unity_logo

이번에는 IDisposable 인터페이스에 대해서 알아보겠습니다. C#은 가비지콜랙터(Garbage Collector)를 가지고 있습니다. 이 GC는 기본적으로 관리되는 모든 객체들의 참조 링크를 관리하며 더이상 참조되지 않는 객체들을 자동으로 메모리에서 소거하는 작업을 수행합니다. 하지만 GC는 창 핸들, 열린 파일, 스트림과 같이 관리되지 않는 리소스들을 인식하지 못합니다.

다음은 문제가 발생할 가능성이 있는 StreamReader의 사용 예 입니다.

StreamReader reader = new StreamReader("content.txt");
string content = reader.ReadToEnd();
// content 활용 코드 수행
reader.Close();

하지만 위의 코드는 ReadToEnd() 메소드를 수행하는 과정에서 오류가 발생할 가능성이 있습니다. 이때에 Close()가 호출되지 않고 반환될 가능성이 있습니다. 이러한 문제를 대응하기 위해 흔히들 try-catch-finally 구문을 사용해 볼 수 있습니다.

StreamReader reader = new StreamReader("content.txt");
try {
	string content = reader.ReadToEnd();
} catch(IOException e) {
	Debug.Log ("Error: " + e.Message);
} finally {
	reader.Close();
}

위의 코드는 ReadToEnd() 메소드를 수행중에 예외가 발생되면 발생한 예외의 내용을 로그에 출력하게 됩니다. 그리고 예외가 발생하거나 성공하거나 상관없이 Close() 메소드를 정상적으로 수행하게 됩니다. 하지만 이러한 복잡한 과정 없이 Close() 호출을 알아서 호출해주는 구문이 있습니다. 위와 같은 방법이 아닌 using 블록을 사용하여 다음과 같이 처리할 수 있습니다.

using (StreamReader reader = new StreamReader("content.txt")) {
	string content = reader.ReadToEnd();
	// content 활용 코드 수행
}

위의 코드에서 우선 눈에 띄는 것은 using 키워드를 사용했다는 점과 Close() 를 명시적으로 호출하지 않고 있다는 부분입니다. 이제 IDisposable에 대해 이야기를 해볼 때인 것 같습니다.

스크린샷 2016-08-11 오후 6.22.38

SteamReader는 TextReader의 자식 클래스입니다. 그리고 이 TextReader는 IDisposable 인터페이스를 구현하고 있습니다. 스크린샷 2016-08-11 오후 6.25.08

이 IDisposable 인터페이스는 Dispose() 메소드 하나만을 가지고 있네요. 이 IDisposable 인터페이스를 구현한 클래스는 Dispose() 클래스를 구현해야 하며 여기서 자신의 메모리 할당 내역을 정리해야 합니다. 위에서 보여준 예제에서 보여지는 StreamReader 역시 Dispose() 클래스를 구현하고 있으며 여기서 리소스를 정리하는 적절한 처리가 되어있을 것입니다.

using (IDisposable 인터페이스를 구현한 클래스 인스턴스 선언) {
    // 리소스 정리가 정상적으로 이루어지지 않을 가능성이 있는 처리
}

이제 위의 코드는 IDisposable 인터페이스를 구현하고 있는 클래스를 인스턴스화 하여 사용하며 using 블록을 나가는순간 (심지어 오류가 발생하더라도) Dispose() 가 호출되어 사용한 리소스가 자동으로 정리됩니다.

그렇다면 using 블록 내부에서 발생한 예외는 어떻게 처리할 수 있을까요? 다음과 같은 방법으로 처리할 수 있습니다.

try {
	using (StreamReader reader = new StreamReader ("file.txt")) {
		// 리소스 정리가 정상적으로 이루어지지 않을 가능성이 있는 처리
	}
} catch(IOException e) {
	Debug.Log ("Error: " + e.Message);
}

using 바깥쪽에 try-catch 문을 사용해도 using 블록을 빠져나갈 때 Dispose() 가 호출됩니다.

참고 : https://msdn.microsoft.com/ko-kr/library/system.idisposable(v=vs.110).aspx

Unity C# – Extension Methods 알아보기

unity_logo

C# 기준 3.0 버전에 추가되었던 멋진 기능중에 확장 메소드(Extension Methods)라는 것이 있습니다. 이 기능을 이용하여 이미 존재하는 타입에 타입을 재정의하거나 상속을 통하지 않고도 새로운 기능을 추가하는 것이 가능합니다.

예를 들어 어떤 문자열(string)이 숫자로 이루어졌는데 아닌지를 확인하고 싶다고 할때, 이런 기능을 수행할 수 있는 새로운 함수를 선언하고 필요할 때마다 이것을 호출하는 방법을 취할 것입니다. 가령 다음과 같은 형태의 유틸리티 클래스를 만들 수 있습니다.

public class MyUtils
{
    public static bool IsNumeric(string s)
    {
        float output;
        return float.TryParse(s, out output);
    }
}

MyUtil이라는 유틸리티성 클래스를 선언하고 클래스를 인스턴스화 하지도 않고 사용할 수 있는 static 메소드 IsNumeric을 만들었습니다. 이제 다음과 같은 방법으로 사용을 할 것입니다.

string test = "4";
if (MyUtils.IsNumeric(test))
    Debug.Log ("Is Numeric: Yes");
else
    Debug.Log ("Is Numeric: No");

하지만 확장 메소드를 사용하게 되면 위와 같은 기능을 string 클래스가 바로 지원하도록 할 수 있습니다. 먼저 static class를 선언하고 원하는 기능을 수행하는 메소드 역시 static으로 선언해 줍니다. 다음과 같은 형태가 될 것입니다.

public static class MyExtensionMethods
{
    public static bool IsNumeric(this string s)
    {
        float output;
        return float.TryParse(s, out output);
    }
}

위의 예제를 보면 다른 일반적인 정적 메소드(Static Methods)와 다른점 한가지는 파라미터 부분에 this 키워드가 사용되었다는 것입니다. 이러한 this를 파라미터로 사용한 메소드가 static으로 선언되었고 그 클래스 역시 static으로 선언이 되었다면 컴파일러는 이 메소드를 string을 위한 확장 메소드라고 인식하게 됩니다.

다시 정리해 보자면 확장 메소드를 선언하기 위한 조건은 다음과 같습니다.

  1. 클래스를 static 으로 선언할 것
  2. 메소드를 static 으로 선언할 것
  3. 메소드의 첫번째 파라미터에 확장할 타입을 this와 함께 선언할 것 (예: string을 확장할 경우 this string)

이제 이렇게 선언한 확장 메소드는 다음과 같이 사용할 수 있습니다.

string test = "4";
if (test.IsNumeric())
    Debug.Log ("Is Numeric: Yes");
else
    Debug.Log ("Is Numeric: No");

만약 추가로 파라미터를 받고 싶다면, this 없이 두번째 파라미터 이상의 선언을 해주시면 됩니다.

public static class MyExtensionMethods
{
    public static int MyDoublePlus(this int i, int x, int y)
    {
    	return i + x + y;
    }
}

int i = 10;
Debug.Log ("Result: " + i.MyDoublePlus(3, 5)); // Result: 18

참고 : http://csharp.net-tutorials.com/csharp-3.0/extension-methods/