먼저 순수한 파라 메트릭 다형성에 대해 이야기하고 나중에 경계 다형성에 들어 갑시다.
파라 메트릭 다형성이란 무엇입니까? 글쎄, 그것은 유형 또는 오히려 유형 생성자가 유형에 의해 매개 변수화 된다는 것을 의미합니다 . 유형이 매개 변수로 전달되므로 유형을 미리 알 수 없습니다. 이를 바탕으로 어떤 가정도 할 수 없습니다. 자, 그것이 무엇인지 모른다면, 용도는 무엇입니까? 당신은 그것으로 무엇을 할 수 있습니까?
예를 들어, 저장하고 검색 할 수 있습니다. 당신이 이미 언급 한 경우입니다 : 컬렉션. 목록이나 배열에 항목을 저장하려면 항목에 대해 아무것도 알 필요가 없습니다. 목록이나 배열은 유형을 완전히 알 수 없습니다.
그러나 Maybe
유형은 어떻습니까? 익숙 Maybe
하지 않은 경우 값이 있고 그렇지 않은 유형입니다. 어디서 사용하겠습니까? 예를 들어, 사전에서 항목을 가져올 때 : 항목이 사전에 없을 수 있다는 사실은 예외적 인 상황이 아니므로 항목이 없으면 예외를 발생시키지 않아야합니다. 대신, 당신의 하위 유형의 인스턴스를 반환 Maybe<T>
정확히 두 개의 하위 유형이있다 : None
와 Some<T>
. 예외 또는 전체 춤 을 던지는 대신 int.Parse
실제로 반환해야 할 무언가의 또 다른 후보입니다 .Maybe<int>
int.TryParse(out bla)
자, 당신 Maybe
은 0 또는 하나의 요소 만 가질 수있는 목록과 같은 일종의 소르 타라고 주장 할 수 있습니다 . 따라서 소장품 모음입니다.
그럼 Task<T>
어때요? 미래의 어느 시점에서 값을 반환 할 것을 약속하지만 현재 가치가있는 것은 아닙니다.
아니면 어떻 Func<T, …>
습니까? 유형에 대해 추상화 할 수없는 경우 한 유형에서 다른 유형으로 함수의 개념을 어떻게 표현 하시겠습니까?
또는보다 일반적으로 추상화와 재사용이 소프트웨어 엔지니어링 의 두 가지 기본 작업 이라는 점을 고려할 때 유형에 대해 추상화 할 수 없는 이유 는 무엇입니까?
이제 경계 다형성에 대해 이야기 해 봅시다. 경계 다형성은 기본적으로 파라 메트릭 다형성과 하위 유형 다형성이 만나는 위치입니다. 형식 생성자가 형식 매개 변수에 대해 완전히 알지 못하는 대신 형식을 특정 형식의 하위 형식으로 바인딩 (또는 제한) 할 수 있습니다 .
컬렉션으로 돌아 갑시다. 해시 테이블을 가져 가십시오. 우리는리스트가 그 요소에 대해 아무것도 알 필요가 없다고 위에서 말했다. 해시 테이블은 해시 할 수 있음을 알아야합니다. (참고 : C #에서는 모든 개체가 동일한 지 비교할 수있는 것처럼 모든 개체를 해시 할 수 있습니다.하지만 모든 언어에 해당되는 것은 아니며 C #에서도 디자인 실수로 간주되는 경우가 있습니다.)
따라서 해시 테이블의 키 유형에 대한 유형 매개 변수를 다음과 같은 인스턴스로 제한하려고합니다 IHashable
.
class HashTable<K, V> where K : IHashable
{
Maybe<V> Get(K key);
bool Add(K key, V value);
}
대신에 이것을 가지고 있다고 상상해보십시오.
class HashTable
{
object Get(IHashable key);
bool Add(IHashable key, object value);
}
당신이 함께 할 것입니다 무엇 value
당신은 거기에서 얻을? 당신은 그것으로 아무것도 할 수 없습니다, 당신은 단지 객체라는 것을 알고 있습니다. 그리고 당신이 그것을 반복한다면, 당신이 얻은 것은 당신이 알고있는 한 쌍 IHashable
(하나의 속성 만 가지고 있기 때문에 당신에게별로 도움이되지 않습니다 Hash
)이고 당신이 알고있는 것이 object
(더 적은 것을 도와줍니다)입니다.
또는 귀하의 예를 기반으로 한 것 :
class Repository<T> where T : ISerializable
{
T Get(int id);
void Save(T obj);
void Delete(T obj);
}
디스크에 저장되므로 항목을 직렬화 할 수 있어야합니다. 그러나 이것을 대신하면 어떻게됩니까?
class Repository
{
ISerializable Get(int id);
void Save(ISerializable obj);
void Delete(ISerializable obj);
}
당신이를 넣어 경우 일반적인 경우에, BankAccount
에, 당신은 얻을 BankAccount
방법 및 특성이 좋아 함께, 다시 Owner
, AccountNumber
, Balance
, Deposit
, Withdraw
, 등 뭔가 당신이 작업 할 수 있습니다. 다른 경우는? 에 넣었 BankAccount
지만 Serializable
속성이 하나만있는를 다시 얻습니다 AsString
. 그걸로 무엇을 하시겠습니까?
경계 다형성으로 할 수있는 몇 가지 깔끔한 트릭도 있습니다.
F- 바운드 정량화는 기본적으로 제약 조건에서 유형 변수가 다시 나타나는 위치입니다. 일부 상황에서는 유용 할 수 있습니다. 예를 들어 어떻게 ICloneable
인터페이스 를 작성 합니까? 반환 유형이 구현 클래스의 유형 인 메소드를 작성하는 방법 MyType 기능 이있는 언어에서는 쉽습니다.
interface ICloneable
{
public this Clone(); // syntax I invented for a MyType feature
}
경계 다형성이있는 언어에서는 다음과 같이 할 수 있습니다.
interface ICloneable<T> where T : ICloneable<T>
{
public T Clone();
}
class Foo : ICloneable<Foo>
{
public Foo Clone()
{
// …
}
}
누군가가 "잘못된"클래스를 형식 생성자로 전달하는 것을 막을 수있는 방법이 없기 때문에 MyType 버전만큼 안전하지는 않습니다.
class EvilBar : ICloneable<SomethingTotallyUnrelatedToBar>
{
public SomethingTotallyUnrelatedToBar Clone()
{
// …
}
}
추상 유형 멤버
결과적으로 추상 타입 멤버와 서브 타이핑이 있다면 실제로 파라 메트릭 다형성 없이도 완전히 동일한 작업을 수행 할 수 있습니다. 스칼라는 시작하는 첫 번째 주요 언어되고,이 방향으로 향하고 와 정확히 예를 들어, 자바와 C #에서 그 반대 인을 제거하는 다음 제네릭합니다.
기본적으로 스칼라에서는 필드와 속성 및 메서드를 멤버로 가질 수있는 것처럼 유형도 가질 수 있습니다. 필드와 속성, 메소드를 추상화하여 나중에 서브 클래스에서 구현할 수있는 것처럼 타입 멤버도 추상화 할 수 있습니다. List
C #에서 지원된다면 다음과 같은 컬렉션으로 돌아가 보겠습니다 .
class List
{
T; // syntax I invented for an abstract type member
T Get(int index) { /* … */ }
void Add(T obj) { /* … */ }
}
class IntList : List
{
T = int;
}
// this is equivalent to saying `List<int>` with generics
interface IFoo<T> where T : IFoo<T>
또한 기억했다 . 그것은 분명히 실제 응용 프로그램입니다. 좋은 예입니다. 그러나 어떤 이유로 나는 만족스럽지 않다. 차라리 그것이 적절할 때와 그렇지 않을 때 내 마음을 사귀고 싶습니다. 여기에 대한 답변은이 과정에 약간의 기여를하지만, 나는 여전히이 모든 것에 대해 불편 함을 느낍니다. 언어 수준의 문제가 이미 오랫동안 귀찮게하지 않기 때문에 이상합니다.