2012-05-11 2 views
1

제네릭 추상 클래스에 대한 몇 가지 문제가 있는데 하위 클래스이며이를 해결하는 방법을 모르겠습니다.일반 추상 클래스 및 제네릭 메서드

public class CommonPoco { 
    private int line; 
    public int Line { 
     get { return line; } 
     set { line = value; } 
    } 

    private string error; 
    public string Error { 
     get { return error; } 
     set { error = value; } 
    } 
} 

지금 나는 또한이 같은 로더의 많은 다른 서브 클래스가 :

public class CountryLoader: Loader<Country> { 
    public override List<Country> LoadFromFile(StreamReader input, 
     out List<Country> result, bool trackProgress, IFormatProvider format) { 
     //Method implementation 
    } 
    public override Country UploadToDb(List<Country> data) { 
     //Method implementation 
    } 

그리고 나는 또한 가지고이 내 CommonPoco입니다

public abstract class Loader<T> where T: CommonPoco { 
    public virtual List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress) { 
     return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture); 
    } 
    public abstract List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format); 
    public abstract T UploadToDb(List<T> data); 
} 

: 나는 다음과 같은 추상 클래스를 Country 클래스를 포함한 CommonPoco의 많은 서브 클래스. 여태까지는 그런대로 잘됐다. 이제 문제를 해결하려면 일반,?, 메서드를 구현하고 싶습니다. 이 메서드는 일부 매개 변수를 기반으로 작업에 대해 올바른 로더를 사용합니다. 이 같은 아마 뭔가 :

void LoadModule<T>(Module module) where T: CommonPoco { 
    Loader<T> loader; 
    switch (module) { 
     case Module.Country: 
      loader = new CountryLoader(); 
      break; 
    } 
} 

이 작동하지 않고 컴파일러는 CountryLoader에서 로더로 변환 수 없다는 불평. 각 모듈을로드하는 메소드를 작성해야하며 Loader 클래스의 초기화를 제외하고는 정확히 동일한 코드입니다. 중복 코드가 정말 싫어. 어떻게 이걸 얻을 수 있니?

.NET Framework 4.0을 사용하는 것을 잊었습니다. 필요한 경우 무엇이든지 변경할 수 있습니다. 필요한 경우 추상 수업조차도 변경할 수 있습니다. 감사. 추상 클래스 대신 인터페이스를 사용하면이 작업을 수행 할 수 있는지 궁금합니다.

+0

Castle Windsor와 같은 IoC 컨테이너를 사용할 수도 있고 반사 효과 만 사용할 수도 있습니다. 하지만 왜 'List '매개 변수가'List '반환 값을 가진 메소드에 있습니까? 그것은 매우 이상하게 보입니다. – phoog

+2

'Loader '('CountryLoader')은'Loader '이 아닙니다. 그래서'Country'는'CommonPoco'에서 상속받을 수 있지만 (위의 OP에서 그 관계가 언급되지는 않았지만) 일반적인 클래스는 상속이 아닙니다. – Tejs

+0

또한 이것은 일반적으로 사람들이 유형이 아닌 특정 데이터 (이 경우'module')를 전달하고 강력하게 형식화 된 결과를 기대할 때 발생합니다. 결과 유형을 판별 할 수없는 경우 해당 데이터에 액세스하는 방}을 다시 생각해 야 할 수도 있습니다. – Tejs

답변

0

이처럼, 인터페이스로 추상 클래스를 설정하면 어떻게 :

public interface ILoader<T> where T : CommonPoco 
    { 
     List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress); 
     List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format); 
     T UploadToDb(List<T> data); 
    } 

는 그런 다음 CountryLoader 구현 변경 :

public class CountryLoader : ILoader<Country> 
    { 
     public List<Country> LoadFromFile(StreamReader input, out List<Country> result, bool trackProgress) 
     { 
      return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture); 
     } 

     public List<Country> LoadFromFile(StreamReader input, 
      out List<Country> result, bool trackProgress, IFormatProvider format) 
     { 
      //Method implementation 
      result = null; 
      return null; 
     } 

     public Country UploadToDb(List<Country> data) 
     { 
      //Method implementation 
      return null; 
     } 
    } 

을 그리고 당신은이 같은 방법 작성 :

void LoadModule<T>(Module module) where T:CommonPoco 
     { 
      ILoader<T> loader; 
      switch (module) 
      { 
       case Module.Country: 
        loader = new CountryLoader() as ILoader<T>; 
        break; 
      } 
     } 

이 문제는 일반적인 LoadFromFile을 구현해야한다는 것입니다. 다른 사람이 주위를 둘러 볼 수있는 방법을 제공하지 않는 한 여러 번.

+1

고마워,이 작품. 명확히하기 위해 인터페이스로 변환 할 필요가 없습니다. 추상 클래스는 잘 작동합니다. 키 라인은 'loader = new CountryLoader() as Loader ;'입니다. – user1387786

0

다음은 불평하는 이유입니다 : Country이 아니지만 다른 국가에서 통과 한 다른 POCO로 처리하려고하면 어떻게됩니까? 예 : LoadModule<Continent>(Module.Country)? 이제 어떻게 될까요? 그것은 당신을 곤경에 빠지게 할이 모호함입니다.

일반 유형의 요점은 전달하는 모든 유형에서 작동하지만 일반적 정의에 관계없이 일반 변수에 특정 유형을 설정하려고한다는 것입니다. 제한 될 때). 일반 제네릭을 유지하십시오. 즉, 일반 정의를 삭제하면됩니다.

void LoadModule(Module module) { 
    Loader<CommonPoco> loader; 
    switch (module) { 
     case Module.Country: 
      loader = (Loader<CommonPoco>)new CountryLoader(); 
      break; 
    } 
} 

는 다형성 뒤에 전제한다는 점에서 당신은 서브 클래스 /해야 할 수있는 일의 "정의"로 인터페이스/기본 클래스를 사용하고 있습니다. 그런 식으로 부모/기본 유형 (이 경우 LoaderCommonPoco)으로 변수를 지정하고 특정 구현 (예 : CountryLoader)은 기능 (메소드 및 속성)의 목적으로 상위/기본 유형으로 처리됩니다.

+0

답장을 보내 주셔서 감사합니다. 시도했지만 작동하지 않습니다. 여전히 같은 문제가 발생합니다. – user1387786

+0

SPFiredrake가 준 코드가 contravariantly 형식을 사용하려고하기 때문에 작동하지 않습니다. Co/Contravariance는 인터페이스에서만 작동합니다. – MgSam

+0

수정되었지만 여전히 작동하지 않습니다. 간단히 말해서,'Country' 객체가'CommonPoco' 객체와 공변하지만,'Loader '객체는'Loader '와 공변하지 않습니다. 예제에서는 인터페이스로 시도했지만,'Loader'는 인터페이스가 아닌 추상 클래스이기 때문에 여전히 마음에 들지 않았습니다. http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx – SPFiredrake

0

공변량 및 반 차별 유형에 문제가 있습니다. 사용중인 C#의 버전에 따라 'in'(공 변) 또는 'out'(반역) 마커로 일반 유형을 표시 할 수 있습니다. 예를 들어 Loader이지만이 경우 클래스가 공변 (안전 확보) 및 반항 (안전 설정) 방식으로 작동하고 있습니다. 두 가지 유스 케이스를 모두 지원하도록 클래스 정의를 분리하거나 유형 결정을 위해보다 정교한 스키마를 사용할 수 있습니다. 상속 된 유형의 개방형 제네릭 해상도를 지원하는 IoC 컨테이너를 사용하는 경우 (Castle Windsor는 그렇지 않습니다. StructureMap에서는 그렇지 않습니다) 컨테이너를 사용하여 주어진 경우에 사용할 로더를 파악할 수 있습니다.

+0

죄송합니다.이 프로젝트에 대해 .net framework 4.0을 사용하여 언급하는 것을 잊어 버렸습니다.그리고 미안 해요 다시 Castle Windsor 또는 StructureMap이 무엇인지 모릅니다. – user1387786

+1

Co/contravariance (in/out)은 클래스가 아닌 인터페이스에서만 사용할 수 있습니다. – MgSam

+0

Castle Windsor 및 StructureMap은 Inversion of Control 컨테이너입니다. 그것들은 객체 생성 책임을 캡슐화합니다 (Factory, Builder 및 Service Locator 패턴을 생각하십시오). 그들은 유형 정의를 조사하여 a) 종속성을 포함하여 그것을 빌드하는 f}과 b) 구성된 모든 룰에 기초하여 주어진 유형에 대한 요청을 충족시키는 f}을 판별 할 수 있습니다. – pfries

0

로더 <의 메소드를 반역자로 표시된 인터페이스로 추상화 해 볼 수 있습니다.

public interface ILoader<out T> 

나서 로더 기본 클래스 구현이 갖는 다음과 ILoader<CommonPoco> 제외 SPFiredrake 기재된 것과 유사한 방법을 사용.

0

하지만 실수로 부를 수있는이

villageLoader = LoadModule<Village>(**countryModuleType**) ; 

같은 그것을 당신이 하나 로더를 예상 할 때 서로를 얻을 컴파일러 오류 일 것이다. @killinaswine의 대답을 참조하십시오. 왜 MS 개발자들은 당신이 그렇게하도록하는 것이 좋지 않다고 생각합니다.

이 문제를 해결하려면 입력 캐스트를 사용해야합니다.

관련 문제