2013-02-19 3 views
0

Это сообщение основано в моем original post со вчерашнего дня. Но я не был достаточно точен относительно моих потребностей, поэтому я попробую снова здесь.Использовать список общих интерфейсов

Пожалуйста, смотрите мой текущий код:

public interface IPosterGenerator<T> 
{ 
    IQueryable<T> GetPosters(); 
} 

public class PetPosterGenerator : IPosterGenerator<PetPoster> 
{ 
    IQueryable<PetPoster> GetPosters() 
    { 
     return busLogic.GetPetPosters(); 
    } 
} 

public class FlowerPosterGenerator : IPosterGenerator<FlowerPoster> 
{ 
    IQueryable<FlowerPoster> GetPosters() 
    { 
     return busLogic.GetFlowerPosters(); 
    } 
} 

public class PinBoard 
{ 
    protected List<IPosterGenerator> PosterGenerators { get; set; } // 1. compiler error 

    public PinBoard(List<IPosterGenerator> generators) // 2. compiler error 
    { 
     this.PosterGenerators = generators; 
    } 

    public List<BasePoster> GetPosters() 
    { 
     var posters = new List<BasePoster>(); 

     foreach (var generator in PosterGenerators) 
     { 
      posters.Add(generator.GetPosters()); 
     } 

     return posters; 
    } 
} 

Моя цель состоит в том, чтобы создать «Pinboard», который может возвращать список плакатов. Каждый плакат может быть другого типа (например, плакат для животных, плакат с цветами и т. Д.). Каждый плакат имеет совершенно другой вид, содержание и так далее. Но все они унаследованы от класса BasePoster.

В общем, у меня будет около 100 различных типов плакатов. Конкретный экземпляр PinBoard может легко содержать 1000 плакатов и более разных плакатов (в разном порядке).

Чтобы заполнить плакаты определенного PinBoard, мне нужно передать PinBoard список определенных генераторов плакатов (количество и тип генераторов будут меняться в зависимости от контекста). Будет один плакат-генератор для каждого типа плаката (например, PetPosterGenerator, который генерирует коллекцию плакатов для домашних животных).

Я думал, что было бы неплохо, если бы все мои генераторы плакатов могли использовать один и тот же (тип безопасного) интерфейса. Вот почему я представил интерфейс IPosterGenerator.

Моя проблема заключается в том, что код не компилируется. Есть две ошибок компилятора с тем же сообщением об ошибке: Использование универсального типа «myApp.IPosterGenerator» требуют 1 аргументов типа

Я не удивлен сообщением об ошибке, потому что я не определения любого типа в этих местах. Было бы замечательно, если бы я мог сделать что-то подобное в строке первой ошибки:

protected List<IPosterGenerator<T>> PosterGenerators { get; set; } 

Но когда я делаю это, компилятор не может найти тип или пространство имен Т.

Теперь я вид потерянного. Возможно, использование универсального интерфейса IPosterGenerator в конце концов не такая хорошая идея. Но я уверен, что в какой-то момент моего приложения мне нужно узнать или получить доступ к конкретному типу плаката, который представляет собой определенный IPosterGenerator.

Как вы, ребята, подходите к этому? Большое вам спасибо за вашу поддержку заранее.

+0

причина вы получаете ошибку довольно очевидно. И один из способов решения этой проблемы, как и кто-то еще, опубликовал, чтобы создать не-общий интерфейс, который реализует общий. Но вам следует подумать над дизайном вашего решения! Там что-то не так. –

+0

@Miky: Вы, вероятно, правы. Кроме того, я понятия не имею, что может быть «не так» ;-) – Ingmar

+0

Проблема в том, что вы смешиваете кучу различных типов объектов «IPosterGenerator» в одну коллекцию. Вы, вероятно, не должны этого делать, потому что, как только вы это сделаете, вы по существу «потеряли» информацию об их общем аргументе. Вам нужно сделать шаг назад и подумать о том, что вам действительно нужно делать, а не как вам нужно это делать или как просто получить код для компиляции, потому что, хотя эти вопросы отвечают, они намного менее вероятны чтобы помочь вам. – Servy

ответ

3

Там нет волшебного решения. Если вы хотите использовать generics, , вам все равно нужно использовать их. Я имею в виду, что вы не можете их избежать.

В другой стороны, вы можете воспользоваться ковариации для того, чтобы позволить IPosterGenerator<T>T параметр принимает BasePoster:

// Check the "out" keyword in the generic parameter! 
// "out" makes the T parameter covariant. 
public interface IPosterGenerator<out T> where T : BasePoster 
{ 
    IQueryable<T> GetPosters(); 
} 

Теперь вы можете сделать это:

public class PinBoard 
{ 
    protected List<IPosterGenerator<BasePoster>> PosterGenerators { get; set; } 

    public PinBoard(List<IPosterGenerator<BasePoster>> generators) 
    { 
     this.PosterGenerators = generators; 
    } 

    public List<BasePoster> GetPosters() 
    { 
     var posters = new List<BasePoster>(); 

     foreach (var generator in PosterGenerators) 
     { 
      posters.Add(generator.GetPosters()); 
     } 

     return posters; 
    } 
} 

Так легко ! :)

+0

Вы имели в виду определение 'where T: BasePoster' вместо': BasePoster'? – Servy

+0

@Servy Ой, да! Исправлена. –

+0

Ничего себе. Это похоже на отличное решение. Мне, наверное, придется немного пожевать его. Спасибо, Матиас !!! – Ingmar

2

Обычно вы определяете не общий интерфейс, а затем наследуете его общий интерфейс. Как так:

public interface IPosterGenerator 
{ 
    IQueryable GetPosters(); 
} 

public interface IPosterGenerator<T> : 
    IPosterGenerator 
{ 
    new IQueryable<T> GetPosters(); 
} 

Для реализации этого бы интерфейс вы могли бы сделать что-то вроде

public class PetPosterGenerator : 
    IPosterGenerator<PetPoster> 
{ 
    IQueryable<PetPoster> GetPosters() 
    { 
     return busLogic.GetPetPosters(); 
    } 

    // Explicit interface implementation on non-generic method 
    IQueryable IPosterGenerator.GetPosters() 
    { 
     // Invokes generic version 
     return this.GetPosters(); 
    } 
} 
+0

Так что я в конечном итоге с классами генератора, как это: общественного класса PetPosterGenerator: IPosterGenerator { IQueryable IPosterGenerator .GetPosters() { певд NotImplementedException(); } IQueryable IPosterGenerator.GetPosters() { throw new NotImplementedException(); } } – Ingmar

+0

Uh. Плохое форматирование. Я прошу прощения. – Ingmar

+0

@IngmarBode Я добавил пример реализации. –

1

Можно с обобщениями:

public abstract class BasePoster { } 
public class PetPoster : BasePoster { } 
public class FlowerPoster : BasePoster { } 

// NOTE the out keyword here. 
public interface IPosterGenerator<out T> where T : BasePoster 
{ 
    IQueryable<T> GetPosters(); 
} 

public class PetPosterGenerator : IPosterGenerator<PetPoster> 
{ 
    public IQueryable<PetPoster> GetPosters() 
    { 
     return Enumerable.Range(0, 5).Select(i => 
     { 
      return new PetPoster(); 
     }).AsQueryable(); 
    } 
} 

public class FlowerPosterGenerator : IPosterGenerator<FlowerPoster> 
{ 
    public IQueryable<FlowerPoster> GetPosters() 
    { 
     return Enumerable.Range(0, 5).Select(i => 
     { 
      return new FlowerPoster(); 
     }).AsQueryable(); 
    } 
} 

public class PinBoard 
{ 
    protected List<IPosterGenerator<BasePoster>> PosterGenerators 
    { 
     get; 
     private set; // fixes compiler warning #1 
    } 

    public PinBoard(List<IPosterGenerator<BasePoster>> generators) // specify the generic type, fixes compiler warning #2 
    { 
     this.PosterGenerators = generators; 
    } 

    public List<BasePoster> GetPosters() 
    { 
     var posters = new List<BasePoster>(); 

     foreach (var generator in PosterGenerators) 
     { 
      posters.AddRange(generator.GetPosters()); // call AddRange not Add 
     } 

     return posters; 
    } 
} 

Тогда это работает:

var generators = new List<IPosterGenerator<BasePoster>>(); 
generators.Add(new FlowerPosterGenerator()); 
generators.Add(new PetPosterGenerator()); 

var pinBoard = new PinBoard(generators); 
+0

Большое вам спасибо за ваше решение и ваши усилия. Я очень ценю это! К сожалению, Матиас был немного быстрее :-( – Ingmar

Смежные вопросы