일반 유형 제약 조건을 언제 사용해야합니까?
-
10-07-2019 - |
문제
기본 클래스가 있습니다.
public abstract class StuffBase
{
public abstract void DoSomething();
}
그리고 두 개의 파생 수업
public class Stuff1 : StuffBase
{
public void DoSomething()
{
Console.WriteLine("Stuff 1 did something cool!");
}
public Stuff1()
{
Console.WriteLine("New stuff 1 reporting for duty!");
}
}
public class Stuff2 : StuffBase
{
public void DoSomething()
{
Console.WriteLine("Stuff 2 did something cool!");
}
public Stuff1()
{
Console.WriteLine("New stuff 2 reporting for duty!");
}
}
좋아, 이제 항목 목록이 있다고 말합니다.
var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());
그리고 나는 그들 모두가 그들의 dosomething () 방법을 호출하기를 원합니다. 목록을 반복하고 그들의 dosomething () 메소드를 호출 할 것으로 기대할 수 있으므로 목록 위에 반복하고 작업을 수행하는 alldosomething ()이라는 방법이 있다고 가정 해 봅시다.
public static void AllDoSomething(List<StuffBase> items)
{
items.ForEach(i => i.DoSomething());
}
다음 방법의 실제 차이점은 무엇입니까?
public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
items.ForEach(i => i.DoSomething());
}
두 방법 모두 실제 용어로는 구문 적으로 다르지만 같은 일을하는 것으로 나타납니다.
그들은 똑같은 일을하는 다른 방법입니까? 제네릭과 유형 제약 조건을 이해하지만이 인스턴스에서 다른 방법을 사용하는 이유를 알 수 없습니다.
해결책
아직 C#이 지원하지 않기 때문입니다. 공분산.
보다 공식적으로, c# v2.0에서 t가 u의 하위 유형 인 경우, t []는 u []의 하위 유형이지만 g는 g의 하위 유형이 아니다 (여기서 G는 일반 유형). 유형 이론 용어에서, 우리는 C# 배열 유형이 "공분산"이고 일반적인 유형이 "불변"이라고 말 함으로써이 동작을 설명합니다.
참조: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx
다음 방법이있는 경우 :
public static void AllDoSomething(List<StuffBase> items)
{
items.ForEach(i => i.DoSomething());
}
var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile
일반 유형 제약 조건을 사용하는 경우.
공분산 및 비밀화에 대한 자세한 내용을 보려면] 확인하십시오. Eric Lippert의 일련의 게시물.
읽을 가치가있는 기타 게시물 :
- http://www.pabich.eu/blog/archive/2008/02/12/c-generics-- parameter-variance-its-constraints-and-how-it.aspx
- http://blogs.msdn.com/rmbyers/archive/2006/06/01/613690.aspx
- http://msdn.microsoft.com/en-us/library/ms228359(vs.80).aspx
- http://www.csharp411.com/convert-between-generic-ienumerablet/
- http://research.microsoft.com/apps/pubs/default.aspx?id=64042
- 왜 나열 할 수 없는가u003Cparent> = 목록u003Cchild> ?
다른 팁
목록이 있다고 가정합니다.
List<Stuff1> l = // get from somewhere
이제 시도하십시오 :
AllDoSomething(l);
일반 버전에서는 허용됩니다. 비 게 니체로는 그렇지 않습니다. 그것이 필수적인 차이입니다. 목록 Stuff1
목록이 아닙니다 StuffBase
. 그러나 일반적인 경우, 당신은 정확히 목록이 될 필요가 없습니다. StuffBase
, 더 유연합니다.
먼저 목록을 복사하여 그 일을 할 수 있습니다. Stuff1
목록으로 StuffBase
, 비 게 릭 버전과 호환되도록합니다. 그러나 방법이 있다고 가정합니다.
List<T> TransformList<T>(List<T> input) where T : StuffBase
{
List<T> output = new List<T>();
foreach (T item in input)
{
// examine item and decide whether to discard it,
// make new items, whatever
}
return output;
}
제네릭이 없으면 목록을 수락 할 수 있습니다 StuffBase
, 그러나 당신은 다음과 같은 목록을 반환해야합니다. StuffBase
. 발신자는 항목이 실제로 파생 된 유형이라는 것을 알고 있다면 캐스트를 사용해야합니다. 따라서 제네릭을 사용하면 실제 유형의 인수 유형을 보존하고 메소드를 통해 리턴 유형으로 채널 할 수 있습니다.
제공된 예에서는 차이가 없지만 다음을 시도하십시오.
List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);
첫 번째 호출은 잘 작동하지만 두 번째 호출은 일반적인 공분산으로 인해 컴파일되지 않습니다.