정리가 필요한 카테고리(추후 정리)/C#,Unity

C#] 제네릭(Generic) - 2

TwinParadox 2017. 2. 2. 00:30
728x90

### 제네릭



## 파생


제네릭 형식도 제네릭이 아닌 클래스처럼 파생 가능.

제네릭 클래스를 기반으로 삼아 파생 클래스 정의 시, 기반 클래스의 형식 매개변수를 열린 채로 남겨두는 것이 가능함.


1
2
class Stack<T> {}
class SpecialStack<T> : Stack<T> {}
cs


구체적 형식을 지정해 제네릭 형식 매개변수를 닫을 수도 있으며, 파생 형식에서 새로운 형식 매개변수를 도입할 수도 있음.


1
2
3
4
class IntStack : Stack<int> {}
 
class List<T> {}
class KeyedList<T,TKey> : List<T> {}
cs


엄밀히 말해 한 파생 형식의 모든 형식 매개변수는 새로 도입된 것임.




## 자신을 참조하는 제네릭 형식


파생 형식이 기반 형식의 형식 매개변수를 닫을 때, 파생 형식 자신을 형식 인수로 지정하는 것도 가능.




## 형식 매개변수와 변환


C#의 캐스팅 연산자는 다양한 종류의 변환을 수행함. 특히 수치, 참조, 박싱/언박싱, 커스텀 변환이 있음.

어떤 변환이 수행될 것인지는 컴파일러가 피연산자들의 알려진 형식에 기초해 컴파일 시점에 결정.

제네릭 형식이나 메서드를 정의하는 코드에서는 형식 매개변수의 구체적 형식이 주어진 상태가 아니라, 컴파일러가 피연산자의 정확한 형식을 알아내지 못하는 경우도 있을 수 잇음. 이로 인해 중의성이 발생하면 컴파일러는 오류를 발생함. 이러한 예시는 아래 코드가 가장 흔함.



1
2
3
4
5
6
StringBuilder Functions<T> (T arg)
{
    if (arg is StringBuilder)
        return (StringBuilder) arg; // 오류
    /* 이하 여백 */
}
cs



컴파일러는 T의 실제 형식을 알지 못해 프로그래머가 의도한 것이 참조 변환이 아닌 커스텀 변환일 수도 있다고 보아, 둘 중 어느 것인지 결정하지 못해 컴파일을 하지 않음.

이러한 문제를 해결하는 가장 간단한 방법은 as 연산자를 이용해 변환을 수행하는 것으로, 이 연산자가 커스텀 변환 수행이 불가능함을 명시하여 컴파일러가 중의성을 해소하게끔 하는 방법이 있음.



1
2
3
4
5
6
7
StringBuilder Functions<T> (T arg)
{
    StringBuilder sb = arg as StringBuilder;
    if(sb != null)
        return sb;
    /* 이하 여백 */
}
cs



좀 더 일반적인 해법은 object로 캐스팅한 후 원하는 형식으로 캐스팅하는 방식이 있음.

object와의 변환은 커스텀 변환이 아닌 참조 변환, 혹은 박싱/언박싱 변환으로 중의성 문제를 해소할 수 있음. 상기 예시에선 StringBuilder는 참조 형식이므로 object를 통한 변환은 참조 변환으로 인식됨.

그러나, 언박싱 과정에서도 중의성이 발생할 수 있음. 아래 예시는 그 사례.



1
int Functions<T> (T x) => (int) x;
cs



이 때 역시 object로 변환한 후 다시 int로 변환하는 방법을 채택하여 언박싱 변환임을 획정지어주면 해결 가능.

728x90