方法返回类型多态

时间:2016-05-19 15:33:11

标签: c# generics polymorphism

我有4个C#课程; JobBuildJobJobFactoryBuildJobfactory。以下是每个班级的MVP。

public class Job {

}

public abstract class JobFactory {
    public abstract List<Job> GetJobs(List<B> bs);
}

public class BuildJob : Job {
    A a;
    B b;

    public BuildJob(A a, B b) {
        this.a = a;
        this.b = b;
    }

}

public class BuildJobFactory : JobFactory {
    A a;

    public override List<Job> GetJobs(List<B> bs) {
        return bs.ConvertAll(b => new BuildJob(a, b));
    }

}

JobFactory用于从B个实例列表中创建一系列作业,这些作业的构建方式取决于其类型(BuildJob,{{1 }} et.c)所以这必须留给工厂类。当我来自Java背景时,我假设从返回类型声明为DestroyJob的方法返回List<BuildJob>会没问题,因为List<Job>扩展BuildJob,但是这在C#中不起作用。

为了缓解这种情况,我尝试了泛型,因此修改后的类看起来就是这样。

Job

我尝试在没有泛型声明(public abstract class JobFactory<T> where T : Job, new() { public abstract List<T> GetJobs(List<B> bs); } public class BuildJobFactory : JobFactory<BuildJob> { A a; public override List<BuildJob> GetJobs(List<B> bs) { return bs.ConvertAll(b => new BuildJob(a, b)); } } )的情况下创建类型JobFactory的变量,因为它在编译时是未知的,因为在运行时变量将被分配给某个类的某个实例它使用一些未知的泛型扩展Jobfactory currentJobFactory;,但这不像在Java中那样工作。

我怎样才能解决其中一个问题?

4 个答案:

答案 0 :(得分:1)

尝试:

Select()

编辑:如果using System.Linq; 不可用,您可能需要将以下内容添加到文件的开头:

Select()

这将指示编译器在System.Linq命名空间中搜索扩展方法(bs.Select()之一)。

这是做什么的:

bs遍历IEnumerable<BuildJob>列表,在每个列表上执行lambda,产生.Cast<Job>()IEnumerable<Job>将其转换为.ToList(),最后IEnumerable<Job>List<Job>转换为IEnumerable<Job>

所有这一切,我会考虑更改您的方法的签名以返回.Cast<Job>().ToList(),这将消除对{{1}}的需求。

答案 1 :(得分:1)

您不能将BuildJobs列表作为作业列表返回,但您可以将BuildJob添加到作业列表中。

例如:

data3

不像LINQ语句那样光滑和内联,但功能齐全。

我同意其余的IEnumerable也是一个更好的返回类型。

答案 2 :(得分:1)

使用IEnumerable<T>IReadOnlyCollection<T>代替List<T>,因为这两个界面都支持协方差。无论如何,返回具体的集合类型被认为是不好的做法,因为它暴露了许多实现细节。

如果您想要延期优惠,请选择IEnumerable<T>;如果您不想,请选择IReadOnlyCollection<T>

public class Job 
{ }

public abstract class JobFactory 
{
    public abstract IEnumerable<Job> GetJobs(IEnumerable<B> bs);
}

public class BuildJob : Job 
{
    A a;
    B b;

    public BuildJob(A a, B b) 
    {
        this.a = a;
        this.b = b;
    }

}

public class BuildJobFactory : JobFactory 
{
    A a;

    public override IEnumerable<Job> GetJobs(IEnumerable<B> bs) 
    {
        return bs.Select(b => new BuildJob(a, b));
    }

}

有关协方差和逆变的更多信息,请参阅:https://msdn.microsoft.com/en-gb/library/mt654055.aspx

答案 3 :(得分:0)

C#中的泛型比Java更严格。您不能忽略通用,并默认解释所有内容都是Object。但是,您可以使用Covariance解决问题。

如果更改通用符,则它看起来像这样:

public abstract class JobFactory<out T>
    where T : Job, new()
{
    public abstract List<T> GetJobs(List<B> bs);
}

然后,您可以毫无问题地自由地将JobFactory<BuildJob>转换为JobFactory<Job>。这是定义IEnumerable<out T>的方式。您可以围绕基本假设为您的核心代码构建逻辑,但需要进一步专业化。

基本上这意味着你可以这样做:

 JobFactory<BuildJob> derivedFactory = new JobFactory<BuildJob>();
 JobFactory<Job> baseFactory = derivedFactory;

或者更直接地,您可以使用如下代码:

 public class Container
 {
     public JobFactory<Job> Factory { get; set; }
 }

以后再分配这样的属性:

 Container myContainer = new Container {
     Factory = new JobFactory<BuildJob>()
 };