2012-06-22 2 views
2

간단히 말해, 사용 된 모든 유형에 상위 유형을 사용하여 배열에 다른 유형 매개 변수를 사용하여 제네릭을 저장하고 싶습니다. MSDN은 제네릭이 불변 타입이기 때문에 불가능하다고 언급했지만, 4.0 프레임 워크 이후로 변경되었다고하는 의견도 있습니다. 다른 제네릭 배열

Cannot implicitly convert type 
'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Dog>' to 
'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Animal>' 

Cannot implicitly convert type 
'InheritanceTest.Program.MyInheritedGeneric<InheritanceTest.Program.Cat>' 
to 'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Animal>' 

부모를 사용하여 배열에 제네릭을 저장할 수있는 방법이 있나요 : 이것은 유사한 오류를 반환

public class Animal 
    { 
    } 
    public class Dog : Animal 
    { 
    } 
    public class Cat : Animal 
    { 
    } 

    public class MyGeneric<T> 
    { } 
    public class MyInheritedGeneric<T> : MyGeneric<T> 
    { } 

    static void Main(string[] args) 
    { 
     MyGeneric<Animal>[] myGenericArray = new MyGeneric<Animal>[] 
     { 
      new MyGeneric<Dog>(), 
      new MyInheritedGeneric<Cat>() 
     }; 
    } 

: 여기

는 내가하고 싶은 것이 기본적인 예입니다 유형의 종류, 또는 이것은 단순히 불가능합니까? 나는 그것이 가능하다는 것을 정말로 희망한다. 그렇지 않으면 내 프로그램이 악몽이 될 것이다 ...

편집 : 좀 더 문맥!

게임에서 적을 생성하는 수업을 만들고 있습니다. 저는 그것들을 Templates라고 부릅니다. (실제 템플릿 클래스와는 관계가 없으며, 청사진이나 공장이라고도 할 수 있습니다.) 적 생성자는 자신의 값을 결정할 때 사용하는 템플릿을 사용합니다. 게임이로드되면 템플릿은 생성하도록 할당 된 해당 유형의 배열을 반환하는 Generate() 함수를 사용하여 모든 적을 생성하는 데 사용됩니다. 템플릿으로 생성되는 모든 객체는 템플릿을 유일한 매개 변수로 사용하는 생성자를 가져야합니다.

public class Template<T> 
{ 
    protected static Random random = new Random(); 
    protected int _amount; 

    public int Amount 
    { 
     get { return _amount; } 
    } 

    public virtual T CreateInstance() 
    { 
     return (T)Activator.CreateInstance(typeof(T), this); 
    } 
    public virtual T[] Generate() 
    { 
     T[] objects = new T[Amount]; 
     for (int i = 0; i < Amount; ++i) 
      objects[i] = CreateInstance(); 
     return objects; 
    } 
} 

다음은 실제 적군 클래스와 템플릿이 포함 된 BasicZombie.cs 파일의 요약입니다.

class Tpl_BasicZombie : Tpl_Enemy<BasicZombie> 
{ 
    public Tpl_BasicZombie() 
    { 
     _hp = 4; 
     _speed = 3; 
     _amount = 10; 
    } 
} 

class BasicZombie : GroundEnemy 
{ 
    public BasicZombie(Tpl_BasicZombie template) 
     : base(template, TextureManager.Get("zombie_base"), 1, 8) 
    { } 

    public void StuffHappens() 
    { } 
} 

게임을로드 할 때 배열의 모든 템플릿을 통해 적을로드하고 싶습니다. 이 작업을 수동으로 수행 할 수 있음을 알고 있지만, 새로운 유형의 적을 생성 할 때마다 수동으로 코드에 추가해야합니다 (따라서 한 번 이상 잊어 버릴 수도 있음).

내 두 옵션은 다음과 같습니다. 1- 일반을 사용하면 위의 문제가 발생합니다. 2 제네릭을 사용하지 않고 내부에 형식을 저장합니다.이 형식은 반환 형식 인 Generate() 함수를 고정합니다. 이것은 generate 함수가 객체의 배열을 출력 함을 의미합니다.이 배열은 템플릿이 적의 배열을 생성 할 때마다 적절한 유형으로 변환해야합니다.

나는이 모든 것에 우아한 해결책이 있다는 것을 알 수있는 공간이 내 머리 속에 있습니다.

+1

[공분산 및 반항 FAQ] (http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx?Redirected=true)를 이미 읽었습니까?)? – 48klocs

+0

나는 그렇지 않다. 나는 한 번 해봤는데 매우 유용했습니다! 이전에이 키워드를 알고 싶었습니다. 공분산을위한 인터페이스를 사용하는 것이 좋습니다. –

답변

1

옆 존 소총의 정보, 당신은 같은 것을 할 수 있습니다

배열 항목 유형을 하나 이상 개최 예정되어있는 경우
public MyGeneric<T2> ToOtherType<T2>() 
{ 
    if (typeof(T2).IsAssignableFrom(typeof(T))) 
    { 
     // todo: get the object 
     return null; 
    } 
    else 
     throw new ArgumentException(); 
} 

     new MyGeneric<Dog>().ToOtherType<Animal>(), 
     new MyInheritedGeneric<Cat>().ToOtherType<Animal>() 
6

예, C# 4는 generic variants을 지원하지만 인터페이스 및 대리자 선언에서만 지원되므로이 경우에는 수행 할 수 없습니다.

public interface IGeneric<out T> 

을하고있는 당신은 IGeneric<Animal>을 만들 수 가리 수업에 그 구현 : 물론 당신은 잠재적으로 인터페이스를 만들 수 있습니다.

달성하고자하는 목표에 대해 자세히 설명해 주시면 대체 방법을 찾을 수 있도록 도와 드리겠습니다.

+0

답변 해 주셔서 감사합니다! 나는 그것을 조사 할 것이다. 또한 위의 내 문제에 대한 자세한 설명을 추가했습니다. 또 다른 질문 : 제네릭 인터페이스 컨텍스트에서 out 키워드의 의미는 무엇입니까? –

+0

@Cristophe :이 경우 'out'은 'T'유형이 인터페이스 구성원의 '출력'(즉, 읽기 전용 속성 또는 메소드의 반환 값)으로 만 나타날 수 있음을 의미합니다. – Gabe

+0

도움 주셔서 감사합니다. covariance에 out 키워드를 사용한 인터페이스를 사용했지만 프로그램에 지저분한 영향을주었습니다 ... 게임 파일을 만들 때 충돌이 발생합니다. Windows phone 용 XNA에서 작업 중이므로 조금 파고 들었고 XNA 프레임 워크가 Windows 용 전체 .NET 4.0 기능 만 지원한다고 읽었습니다 ... 아, 적어도 몇 가지 사실을 배웠습니다 :) –

1

의 항목에 저장해야 할 것됩니다 배열 자체와 분리 된 힙 객체 (일부 유형이 구조체 인 경우 일반 형식에서 파생되는 제네릭 유형의 필드로 상자에 저장하거나 필드에 저장해야 함). 대부분의 경우 가장 간단한 방법은 배열에 저장하는 모든 항목에 공통 조상 유형을 식별하고 필요에 따라 배열 요소를 단순히 형변환하는 것입니다. 그러나 실현 될 수없는 몇 가지 경우가 있습니다. 예를 들어 컬렉션에 알 수없는 객체가 있지만 둘 이상의 인터페이스에 제약이있는 객체를 보유하려는 경우 메서드 유형 매개 변수가 비슷한 제약을받는 일반 루틴에 해당 객체를 전달해야 할 수 있습니다. 루틴을 통과하면 모든 제약 조건을 충족시키는 공통 조상이 없으므로 컬렉션의 모든 구성원을 캐스팅하여 적절한 제네릭으로 전달할 수있는 단일 유형이 없습니다.

컬렉션의 개체가 소수의 루틴에만 전달되는 경우 항목을 추가하는 일반 메서드를 사용하여 필요한 모든 루틴을 적절히 호출하고 해당 대리자를 수집. 람다 표현식이나 익명의 위임자가 편리 할 수 ​​있습니다.

예를 들어

, 하나는 다양한 IWibbler 객체의 Wibble<T> 방법과 T 종류의 인터페이스 제약이 다양한 IWobbler 객체의 Wobble<T> 방법 목록에 저장되어있는 항목을 먹일 수 있어야합니다 가정 I1I2 .

 
    interface IWibbler { void Wibble<T>(T param, int param) where T : I1,I2; } 
    interface IWobbler { void Wobble<T>(T param, string param) where T: I1,I2; } 

    private struct WibbleWobbleDelegateSet 
    { 
     public Action<IWibbler, int> Wibble; 
     public Action<IWobbler, string> Wobble; 
     static WibbleWobbleDelegateSet Create<T>(T param) where T: I1, I2 
     { 
      var ret = new WibbleWobbleDelegateSet(); 
      ret.Wibble = (IWibbler wibbler, int p2) => { wibbler.Wibble<T>(param, p2); }; 
      ret.Wobble = (IWobbler wobbler, string p2) => { wobbler.Wobble<T>(param, p2); }; 
      return ret; 
     } 
    } 

IWibbler.Wibble<T>() 또는 IWobbler.Wobble<T>() 방법 구조체 생성에 제공된 파라미터를 전달하는데 사용될 수 위임을 포함하는 비 일반적인 구조를 수득한다하는 param 적절 제약과 WibbleWobbleDelegateSet.Create<T>(T param)를 호출.

이 접근법은 호출 할 루틴 목록을 알고있는 경우에만 직접 사용할 수 있습니다. 제한된 일반 매개 변수를 사용하여 임의의 루틴을 호출 할 수 있어야하는 경우 까다로운 인터페이스 나 Reflection을 사용하여 루틴을 수행 할 수 있지만 이러한 작업은 더욱 복잡해집니다.

관련 문제