본문 바로가기

.NET/C#

[C#] using 구문 사용법

using 키워드는 단순히 네임스페이스를 임포트할 때 뿐만 아니라 다른 용도로도 사용이 가능한데, using의 유용한 두 가지 기능을 소개한다.

 


1. 별칭 선언

using 키워드를 사용해 특정 타입의 별칭을 선언할 수 있다. 이는 c나 c++의 typedef와 유사하다.

using Element = System.Int32;

class Program
{
    static void Main(string[] args)
    {
        Element element = 15;
        Console.Write(element.ToString());
    }
}

 

그다지 사용될 일이 없어보여도 은근히 사용하면 유용할 경우가 생긴다. 가령 아래와 같이 여러 개의 클래스가 중첩되어 있는 상황에서 내부 클래스를 사용 및 접근하기 위해 외부 클래스의 이름을 줄지어 늘어놓아야 하는 경우이다.

 

public class Protocol
{
    public class Packet
    {
        public class SubPacket
        {
            public class DataField
            {
                private int _data;
                
                public static void SetData(DataField dataField, int data)
                {
                    dataField._data = data;
                }
                
                public static void GetData(DataField dataField)
              	{
                    return dataField._data;
                }
            }
        }
    }
}

static void Main(string[] args)
{
    var data1 = new Protocol.Packet.SubPacket.DataField();
    Protocol.Packet.SubPacket.DataField.SetData(data1, 15);
    
    Console.Write(Protocol.Packet.SubPacket.DataField.GetData(data1).ToString());
}

별칭을 선언하여 아래와 같이 사용할 수 있다.

using DataField = Protocol.Packet.SubPacket.DataField;

public class Protocol
{
    public class Packet
    {
        public class SubPacket
        {
            public class DataField
            {
			    private int _data;
                
                public static void SetData(DataField dataField, int data)
                {
                    dataField._data = data;
                }
                public static void GetData(DataField dataField)
              	{
                    return dataField._data;
                }
            }
        }
    }
}

static void Main(string[] args)
{
    var data1 = new DataField();
    DataField.SetData(data1, 15);
    
    Console.Write(DataField.GetData(data1).ToString());
}

 

또는 여러 네임스페이스를 참조해서 사용해야 하는 상황에서 각기 다른 네임스페이스에 있는 동일한 이름의 타입을 사용해야 할 경우가 있다. 예를 들어 System.Drawing.Imaging 네임스페이스의 Point 구조체와 OpenCvSharp의 Point 구조체를 둘 다 사용해야 한다면?

 

using을 통해 두 네임스페이스를 모두 참조하게 되어도 Point 구조체를 코드에서 사용하려고 하면 "'System.Drawing.Imaging.Point'와 'OpenCvSharp.Point' 간의 모호한 참조입니다." 같은 오류가 발생할 것이다. 이러한 경우 Point가 어느 곳의 Point인지를 확실히 하기 위해 앞에 전체 네임스페이스를 매번 적어줄 필요가 있는데, using으로 별칭을 선언하면 간결하게 해결할 수 있다.

using OpenCvSharp;
using System.Drawing;

using CvPoint = OpenCvSharp.Point;
using DrawingPoint = System.Drawing.Point;

static void Main(string[] args)
{
    var p1 = new CvPoint(1, 2);
    var p2 = DrawingPoint.Empty;
}

 

 


2. 자동 Dispose로 메모리 관리

using문을 사용하여 IDisposable 객체의 Dispose()를 자동으로 호출해서 메모리 관리를 편하게 할 수 있다.

(Dispose() 메서드는 객체가 사용중인 메모리와 리소스를 해제하는 메서드이다.)

 

using (var form = new Form1())
{
    form.ShowDialog();
}

위와 같은 방법으로 사용할 수가 있는데, using의 블록이 끝날 때, 혹은 블록이 끝나기 전에 예외가 발생했으나 예외를 처리하지 않았을 때 using 키워드로 생성된 객체(위 코드에서는 Form1 form)의 Dispose() 메서드를 호출하여 메모리를 해제해준다.

즉, 위 코드는 내부적으로 아래 코드로 변환되며, 아래의 코드와 완전히 같은 동작을 한다.

var form = new Form1();
try
{
    form.ShowDialog();
}
finally
{
    form.Dispose();
}

 

using 문은 개체의 사용이 끝남과 동시에 Dispose() 메서드를 호출해주는 구문이기 때문에 IDisposable 인터페이스를 구현하는 클래스의 개체에 대해서만 사용이 가능하다.

 

StreamReader와 StreamWriter로 파일을 읽고 쓴 후에는 반드시 Close() 메서드를 호출해서 스트림을 닫아주는 것이 좋은데, using 구문을 통해 사용하게 되면 Dispose() 메서드를 자동으로 호출해주므로 Close()를 깜빡하고 호출하지 않는 실수, 일일히 호출해야 하는 번거로움을 줄일 수 있다.

using (var sr = new StreamReader(path))
{
    MessageBox.Show(sr.ReadToEnd());
}