반응형

C# : using문의 기능과 활용

알게 된 배경

 C++에서 개발을 하다가 C#을 접하게 되었을 때 익숙하면서도 생소한 문법이 using문이었다. C++에서도 namespace를 선언할 경우 'using namespace [이름];' 형태의 문법을 사용하고 C#에서도 마찬가지로 최상단에 'using System;' 형태로 기본적으로 작성이 되어 있기 때문이다.


 하지만, 입문을 제대로 하지 않은 상태에서 파일, 네트워크 등 IO(입출력)에 해당되는 MSDN 문서를 보면 코드의 메서드 안에서 사용되는 using문을 쉽게 볼 수 있다. 하지만, 기존에 using 사용되던 키워드(?)와 다른 기능이라서 처음에 쉽게 사용하기 어렵다.


 특히나 예외처리 문법의 finally 문의 기능과 유사했기 때문에 필자의 경우 예제를 보기 전까지는 제대로 활용을 하지 못 했었다.


using문의 의미

 키보드와 콘솔 화면 출력을 제외한 대부분의 IO(입출력)의 경우 실제로 stream이나 unmanagement 메모리 공간을 할당하고 해제 하는 패턴 혹은 Stream Open 과 Stream Close 하는 패턴으로 코드를 작성해야 한다.


 C++의 경우 소멸자가 유용하게 사용되는데, C# 역시 소멸자를 제공하지만, IDisposeable 인터패이스를 상속하여 Dispose라는 메서드를 override하여 Unmanagement 메모리 해제나 Stream Close를 구현하도록 권장을 하고 있다. 그리고 이렇게 구현된 Dispose 메서드는 using문의 Scope에서 벗어나면, 호출이 하도록 하여 자연스럽게 C++의 소멸자와 비슷한 기능을 수행한다.


using문 예시


using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    using(var imgFileStream = new FileStream(imgFilePath, FileMode.Open))
    {
        var tmpImg = Image.FromStream(imgFileStream);
        // ... 중략
    }
}



예외처리의 finally문과의 관계

 IO처리에 있어서 항상 염두해야 하는 문법은 예외 처리이다. 이는 파일 입출력의 경우 파일을 읽을 때 파일이 없는 경우, 통신중에 네트워크 연결이 끊어진 경우 등 비정상적인 동작을 대비해서 작성하는 문법이다.


 여기서 finally 문의 경우 예외가 발생하던 안하던 간에 반드시 실행되는 내용을 작성을 하도록 되어 있다. 가령 Stream Open 한뒤에 작업중에 예외가 발생해서 파일을 닫아야 하지만, 예외 발생 하지 않은 경우에도 닫아야 하는 경우에 사용된다.


try-catch-finally문 예시

using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    try
    {
        var imgFileStream = new FileStream(imgFilePath, FileMode.Open);
        var tmpImg = Image.FromStream(imgFileStream);
        // ... 중략
    }
    catch(Exception ex)
    {
        // ... 예외 처리
    }
    finally
    {
        imgFileStream.Close();
        // ... 혹은 imgFileStream.Dispose() 호출
    }
}




 여기서 생각을 가만히 해보면 finally문이 using문과 역할의 유사점을 느낄 수 있다. 오히려 finally문의 경우 Close 같은 메서드를 직접 호출하는 반면, using문은 IDisposeable을 상속 받은 객체의 경우 자동으로 Scope를 벗어나면 Dispose() 메서드를 호출한다.


 그리고 MS의 대부분 Stream에 관련된 클래스들은 Dispose() 메서드에 이러한 기능들이 정의 되어 있다. 따라서 IO 구현시 가능하면 using문을 사용하는 것이 실수를 예방하기에 좋은 편이다.



using문/try-catch 패턴 예시


using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    using(var imgFileStream = new FileStream(imgFilePath, FileMode.Open))
    {
        try
        {
            var tmpImg = Image.FromStream(imgFileStream);
            // ... 중략
        }
        catch(Exception ex)
        {
            // ... 예외 처리
        }
    }
}


 위의 패턴으로만 보았을 때 using문-try-catch 패턴이 깔끔해보인다. 하지만, 처음 using문에서 사용할 객체를 만드는 순간에 발생하는 예외의 경우 처리가 안되는 패턴이기 때문에 이를 피하기 위해서 파일 존재 여부 검사와 같은 추가 코드가 필요하다.



참고자료

도서: C# 7과 닷넷 코어 2.0


반응형

'C#' 카테고리의 다른 글

C# .NET: Dispose (소멸자) 패턴  (0) 2018.05.14
C# .NET : SQLLocalDB (1) 개요와 오류사례  (0) 2018.03.23
C# : this와 base  (0) 2018.02.20
C# Winform : Detect Resize (크기 변화 감지)  (0) 2018.02.07
C# : Queue VS ConcurrentQueue  (0) 2018.02.02
반응형

C++ : 네임스페이스

정리하게 된 배경

 네임스페이스는 C와 C++의 표준함수의 가장 큰 차이점을 보이는 기능중 하나이다. 때문에 명확히 알고 활용을 해야 하기 때문에 정리한다.

 기초이지만, 중요한 개념이기도 하다. 또한, 학교나 학원 그리고 혼자 공부하는 사람입장에서는 예제에서 많이 적용하기 않기 때문에 의외로 활용을 못하는 초보 프로그래머를 많이 볼 수 있다.


개념

 프로그램이 커지고 복잡해지면서, 작명의 한계(?)로 인해서 발생되는 문제를 해결하기 위한 개념으로 등장을 했다. 이후 자바스크립트(JavaScript)나 파이썬(Python) 같은 스크립트계열 언어에서는 모듈이라는 이름으로 발전한다.

 C++에서는 네임스페이스(namespace)를 통해서 같은 이름의 클래스(class)명이나 함수(function)명을 선언 및 정의가 가능해졌다. 때문에 이로 인해 프로그래머들은 작명의 스트레스가 줄게 되었다.


사용방법

 네임스페이스는 클래스를 선언하는 방식과 비슷하게 namespace 키워드 바로 뒤에 선언하고자 하는 네임스페이스 명을 붙여서 선언을 하고 나서 중괄호 안에서 선언 및 정의를 하면 된다.

// A.h
namespace hg
{
    class A
    {
        // ...
        void func();
    }
}

네임스페이스의 내부에 선언 및 정의된 것들(함수, 변수, 클래스 등)을 호출하여 사용할 경우에는 앞에서 선언한 네임스페이스 이름 뒤에 "::" 붙여서 선언할 수 있다.


// A.cpp
// 클래스의 함수정의
#include "A.h"

void hg::A::func()
{
    // ...
}
// main.cpp
// 클래스의 함수호출
#include "A.h"

int main(int argc, char* argv[])
{
    hg::A a;
    a.func();
    return 0;
}

 사용할때 마다 네임스페이스 이름을 매번 적는 것은 상당한 노동과 타이핑양이 많기 때문에 기본적으로 네임스페이스 이름을 사용중인 상태를 나타내는 using 키워드를 사용하면 된다.


// A.cpp
// 클래스의 함수정의
#include "A.h"
using namespace hg;

void A::func()
{
    // ...
}
// main.cpp
// 클래스의 함수호출
#include "A.h"
using namespace hg;

int main(int argc, char* argv[])
{
    A a;
    a.func();
    return 0;
}

네임스페이스의 내부에 선언 및 정의된 것들(함수, 변수, 클래스 등)을 호출하여 사용할 경우에는 앞에서 선언한 네임스페이스 이름 뒤에 "::" 붙여서 선언할 수 있다.

 여기서 using namespace hg;의 유효범위는 해당 키워드가 사용된 파일내에서는 유효하다. 즉, 다른 cpp(혹은 cc) 파일에서는 다시 using 키워드를 사용해서 네임스페이스 사용중임을 알려야 한다.


주의 할점

 개념이 잡힌 사람에게는 당연한 말이 겠지만, using 키워드를 이용한 네임스페이스 선언을 할 경우 헤더 파일에서는 사용하지 않는 것이 정석이다. 따라서 네임스페이스는 정의는 보통 헤더 파일에서 된다. 그리고 using 키워드의 경우 헤더의 클래싀 정의 할 경우에 사용된다. 여러 네임스페이스에서 정의된 것들은 같이 써야 될 경우 가능하면, using은 사용하지 않는 것이 좋다.

 헤더파일에서 using을 쓰면 안되는 이유는 논리적으로 헤더파일은 cpp파일에서 include 될때마다 포함되는데, 이는 include 하는 cpp 마다 강제로 using 키워드를 사용한것처럼 되어버리기 때문에 사실상 네임스페이스가 없는 것처럼 되어 버린다.


추가사항

 헤더파일을 include 할 경우 C와 C++의 몇 가지 차이점이 있는데, C의 stdio.h의 경우 C++에서는 cstdio로 include하게 된다. 이 뿐만 아니라 기존의 C 표준 헤더 파일들 앞에 'c'를 붙어 있는 것을 알 수 있다.

 이 두 헤더 파일간의 차이는 바로 네임스페이스의 존재의 차이가 있다. C에서는 네임스페이스 키워드가 없지만, C++에서는 네임스페이스가 있어서 표준함수 네임스페이스인 std내에 표준함수들이 정의 되어 있다.(물론 C에는 네임스페이스가 없어도 있는 것처럼 사용할 수 있다.)

반응형

+ Recent posts