오늘은 C# 문법에서 잘 사용하지 않는 연산자에 대해 생각해 봤다.
코딩을 하면서 정말 잘 사용하지 않는 연산자들로는 시프트 연산자 와 비트 연산지 인데, 오랜만에 공부를 하면서 사용하면 어떤식으로 사용을 할까 알아보다가 velog에 남겨야겠다 싶어서 오늘 TIL로 작성하게 되었다.
비트 연산자는 공식 문서에는 비트 보수 연산자 딱 하나만 있다.
int a = 5; // 이진수로 0000 0101
int result = ~a; // 이진수로 1111 1010 (보수 결과), 10진수로 -6
Console.WriteLine(result); // 출력: -6
위 코드에서 알 수 있듯이 이진수 값을 비트 보수 연산자로 변환 해주면 변수의 비트값을 반대로 바꿔버리는 기능을 한다.
특정 비트 플래그를 토글하는데 사용할 수 있다.int flags = 0b0000_1111; // 초기 플래그 상태
// 세 번째 비트를 반전시킴
int mask = 0b0000_0100;
flags = flags ^ mask;
Console.WriteLine(Convert.ToString(flags, toBase: 2)); // 출력: 00001011
flags변수에 00001111 라는 초기 플래그 상태로 선언되어 있는데 이것을 mask 변수의 이진수 값 00000100 라는 값으로 XOR 연산자(^)를 사용해서 3번째 비트를 토글 할 수 있게 된다. 그럼 결과적으로 flags 변수의 값은 00001011 라는 값으로 변환 된 것을 확인 할 수 있다.
이런 식의 토글방법은 게임 프로그래밍에서 유닛의 상태변화에 주로 사용된다.
권한 기능에서 특정 권한만 토글하는 방식으로 사용 할 수 있다.흔히 웹개발에서는 잘 사용하지 않는 방식이긴 하지만 리눅스에서 사용하는 권한 방식과 유사하다.
uint permissions = 0b1111_1111; // 모든 권한을 부여한 상태라고 보자.
uint readPermissionMask = 0b0000_0001; // 읽기 권한 mask
permissions = permissions & ~readPermissionMask; // 모든 권한에서 읽기 권한만 해제한 상태
Console.WriteLine(Convert.ToString(permissions, toBase: 2)); // 출력: 11111110
permissions 변수에 11111111 값을 선언하고 readPermissionMask 값 00000001로 AND 논리 연산과 비트 보수 연산을 해주면 나머지 mask의 1인 값을 제외한 나머지는 그대로 있고 1인 값만 0으로 바뀐다.
앞서 설명 했듯이 리눅스 권한 방식과 유사한 방식이다.
그 밖에 비트 보수 연산자를 사용하는 곳은 finalizers(종료자)에 사용된다.
finalizer란 클래스에서 인스턴스를 생성하고 이후에 리소스가 해제 될때 (가비지 컬렉터에 의해 해제 될때) 호출되는 함수라고 생각하면 된다.
class MyClass
{
// 종료자 선언
~MyClass()
{
// 리소스 해제 될 때 코드
System.Diagnostics.Trace.WriteLine("MyClass finalizer is called.");
}
}
언뜻 보면 Dispose 패턴과 유사하긴 하지만 호출 시점이 다르다고 한다.
또한 IDisposable 과 혼용해서 사용 할 수 있다.
class ResourceHolder : IDisposable
{
private bool disposed = false;
// 관리되지 않는 리소스
private IntPtr unmanagedResource;
public ResourceHolder()
{
// 리소스 할당
unmanagedResource = AllocateResource();
}
~ResourceHolder()
{
// 종료자에서 Dispose 호출
Dispose(false);
}
public void Dispose()
{
Dispose(true);
// 가비지 컬렉터에서 종료자를 호출하지 않도록 설정
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
// 관리되지 않는 리소스 해제
FreeResource(unmanagedResource);
if (disposing)
{
// 관리되는 리소스 해제
}
disposed = true;
}
}
}
가끔 코딩을 하다보면 unmanagedResource 같이 관리되지 않는 리소스 같은 경우 직접 자원을 해제 해줘야하는 경우가 생긴다. 이런 경우에 class의 인스턴스의 자원을 해제 할때 종료자(~)에서 Dispose 를 unmanagedResource의 자원을 해제 하는 것을 알 수 있다.
여기까지 보수 비트 연산자에 대해 알아 보았다.
그 외에 비트를 조작하는 시프트 연산자가 여러개 있는데 이것들은 추후 정리해 보도록하겠다.