C#의 존재하는 모든 클래스의 기본이 되는 자료형을 말하는데
object 형식을 사용하면 데이터의 형식에 관계없이 object 형식의 자료형에 담을 수 있습니다.
그렇지만 참조 형식으로만 저장하여 해당 데이터를 힙에 할당하고, 주소 값만 담고 있기 때문에, 데이터를 다시 활용하기 위해서는 값 형식으로 변환하는 과정이 필요합니다.
// object 자료형 (모든 자료형을 담을 수 있다!)
object obj1 = 1;
object obj2 = "안녕";
object obj3 = true;
object obj4 = 1.54;
object 형식은 모든 자료형의 최상위 부모라고 생각하시면 됩니다.
즉, 상속과 마찬가지로 기본적으로 다운캐스팅이 안되기 때문에 명시적으로 변환해야 합니다
object left = 10;
object right= 10;
int result = left + right; // 구문 오류가 뜸
object left = 10;
object right = 10;
int result = (int)left + (int)right; // (int)를 붙여 명시적으로 변환함
Console.WriteLine(result);
값 형식에서 참조형식으로 변환하는 과정을 박싱이라고 합니다.
어떤 크기를 필요로 할지 몰라 데이터를 복사해 힙 메모리에 할당합니다.
int value = 10;
// 박싱 : 값형식 → 참조형식
object obj = value;
박싱과 반대되는 개념으로 참조 형식에서 값 형식으로 변환하는 과정을 언박싱이라고 합니다.
데이터를 복사해 스택 메모리에 할당하는 방식이며 명시적으로 선언해야 사용 가능합니다.
int value = 10;
// 박싱 : 값형식 → 참조형식
object obj = value;
// 언박싱 : 참조형식 → 값형식
int result = (int)obj;
박싱과 언박싱을 과정을 거치게 되면 엄청 느려져 성능하락으로 이어지기 때문에
왠만하면 최적화를 위해서는 피하는 것이 좋습니다.
object 형식을 이용한 형변환은 박싱과 언박싱 과정을 거치면서 성능 저하가 있고 해당 형식으로의 변환이 적절한지에 대한 판단없이 진행되므로 코드가 위험해질 수 있기 때문에 피해야 한다고 했습니다.
제네릭은 박싱과 언박싱 단계를 거치지 않고 형식을 조정하므로 성능이 향상되고, 컴파일 단계에서 올바른 데이터 형식이 적용되므로 형식 안정성이 보장됩니다.
static void Main(string[] args)
{
int a = 10;
int b = 20;
Util.Swap(ref a, ref b);
Console.WriteLine("a = {0}, b = {1}", a, b);
}
public class Util
{
public static void Swap(ref int left, ref int right)
{
int temp = left;
left = right;
right = temp;
}
}
a = 20, b = 10
이 코드에서 다른 자료형으로 바꿀 때 함수를 더 만들 수도 있고 object를 사용할 수도 있지만
상당히 비효율적일 수 밖에 없기 때문에 제네릭을 사용해야 합니다.
사용할 때 <>를 사용하여 배치합니다.
보통 프로그래밍 국룰로 T를 사용한다고는 하는데 아무 글자 사용해도 동작은 합니다.
public static void Swap<T>(ref T left, ref T right)
{
T temp = left;
left = right;
right = temp;
}
Main에는 바꾸고자 하는 자료형을 <>에 적어주면 됩니다.
static void Main(string[] args)
{
int iLeft = 10;
int iRight = 20;
Util.Swap<int>(ref ileft, ref iright);
Console.WriteLine("a = {0}, b = {1}", iLeft, iRight);
float fLeft = 10.56;
float fRight = 20.87;
Util.Swap<float>(ref fleft, ref fright);
Console.WriteLine("a = {0}, b = {1}", fLeft, fRight);
bool bLeft = true;
bool bRight = false;
Util.Swap<bool>(ref bLeft, ref bRight);
Console.WriteLine("a = {0}, b = {1}", bLeft, bRight);
}
a = 20, b = 10
a = 20.87, b = 10.56
a = False, b = True
다만 일반화는 기본적으로 산술연산, 비교연산, 논리연산을 못합니다. 대입연산만 가능합니다.
public static T Add <T>(T left, T right)
{
return left + right; // 오류
}
where을 통해 제약을 걸어 구조체만, 클래스만 사용가능하도록 제한하는 기능입니다.
public static void Test<T>(T value) where T : struct
{
}
// 구조체만 사용 가능
public static void Test<T>(T value) where T : struct { }
// 클래스만 사용 가능
public static void Test<T>(T value) where T : class { }
// T열거형만 사용 가능
public static void Test<T>(T value) where T : Enum { }
// 매개변수 없는 생성자가 있는 자료형만 사용 가능
public static void Test<T>(T value)where T : new() { }
// 인터페이스를 포함한 자료형만 사용 가능
public static void Test<T>(T value) where T : IComparable { }