0

这篇文章基于我昨天的原始帖子。但我对我的需求不够精确,所以我会在这里再试一次。

请查看我当前的代码:

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; }

但是当我这样做时,编译器找不到类型或命名空间 T。

现在我有点迷路了。也许使用通用 IPosterGenerator 接口毕竟不是一个好主意。但我确信在我的应用程序中的某个时刻,我需要知道或访问某个 IPosterGenerator 所代表的具体海报类型。

你们将如何处理这个问题?非常感谢您提前的支持。

4

3 回答 3

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();
    }
}
于 2013-02-19T16:16:20.277 回答
2

没有神奇的解决方案。如果你想使用泛型,你仍然需要使用它们。我的意思是你无法避免它们。

另一方面,您可以利用协方差来让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;
    }
}

太简单!:)

于 2013-02-19T16:25:28.310 回答
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);
于 2013-02-19T16:28:25.247 回答