在没有泛型的派生类中重新定义方法返回类型

时间:2010-06-26 23:23:03

标签: c# inheritance partial-classes llblgen

TL; DR:

  

是否有某种方法可以向基类添加抽象方法,允许派生类覆盖方法的返回类型,而不使用泛型,并且不使用new关键字?


我正在为LLBLGen Pro开发一些自定义模板。在此过程中,我拒绝更改LLBLGen Pro提供的默认模板,因此如果他们选择实施我的模板,我的方法不会覆盖其他人的文件。

我开始研究的一项任务(并取得了很好的进展)正在开发一个模板,为每个实体生成一个DTO。沿着这些方向,一个目标是为我的实体提供ToDTO()方法。为了通用编程,我决定在一个公共基类中定义这个方法,这就是我的麻烦开始的地方。


请记住,在基类中定义ToDTO()方法的目的是因为我希望创建一个通用存储库(例如,使用Fetch()方法)我希望将工作从CommonEntityBase开始,而不是特定的实体。


LLBLGen定义了它的CommonEntityBase类,如下所示:

public abstract partial class CommonEntityBase : EntityBase2 {
   // LLBLGen-generated code
}

我最初的计划是将我的方法添加到另一个部分类中,如下所示:

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

我认为继承的类能够将它们的方法中的返回类型定义为从基类的返回类型派生的类型,如下所示:

public partial class PersonEntity : CommonEntityBase {
    public override PersonDTO ToDto(){ return new PersonDTO(); }
}

但是I was wrong


我的第二次尝试是使用泛型来定义类,如下:

public abstract partial class CommonEntityBase<T> : CommonEntityBase 
      where T : CommonDTOBase {
   public abstract T ToDto();
}

足够简单。我所要做的就是让我生成的实体类继承自这个新的实体库。只是一个警告。因为我不想覆盖LLBLGen的模板,所以它又回到了部分类。

LLBLGen的个体实体有这样的定义:

public partial class PersonEntity : CommonEntityBase {
   // LLBLGen-generated code
}

这就是我的问题。为了使我的方法有效,我将不得不用这个定义创建我自己的部分类:

public partial class PersonEntity : CommonEntityBase<PersonDTO> {
   public override PersonDTO ToDto(){ return new PersonDTO(); }
}

当然,这是不可能的,因为as I now know

  

指定基类的[部分类]的所有部分必须同意,但省略基类的部分仍然继承基类型。


我要尝试的第三件事就是用new关键字覆盖基类的函数定义:

public abstract partial class CommonEntityBase {
    public virtual CommonDTOBase ToDto(){ return null; }
}

public partial class PersonEntity : CommonEntityBase {
    public new PersonDTO ToDto(){ return new PersonDTO(); }
}

然而,这完全违背了我的方法的目的,因为我希望能够在PersonEntity转换为ToDTO()时访问CommonEntityBase的{​​{1}}方法。通过这种方法,做:

CommonEntityBase e = new PersonEntity();
var dto = e.ToDto();

会导致dto为空,这是我不想要的。


我遇到various links讨论我的第一种方法,以及为什么它不起作用,并且通常将我的通用方法指向一般意义上的解决方案。但是,在我的情况下,泛型似乎不起作用。


这一切都是为了询问我是否有可能实现的目标。

是否有某种方法可以向基类添加抽象方法,允许派生类覆盖方法的返回类型,而不使用泛型,并且不使用new关键字?< /强>

或者我可能是从错误的角度接近这个问题,还有其他一些技术可以解决我的问题吗?


修改

以下是我希望通过Porges's方法完成实体的用例:

public class BaseRepository<D,E> where D : CommonDTOBase where E : CommonEntityBase,new
    public D Get(Guid id){
        var entity = new E();
        entity.SetId(id);

        // LLBLGen adapter method; populates the entity with results from the database
        FetchEntity(entity);

        // Fails, as entity.ToDto() returns CommonDTOBase, not derived type D
        return entity.ToDto();
    }
}

2 个答案:

答案 0 :(得分:4)

而不是:

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

public partial class PersonEntity : CommonEntityBase {
    public override PersonDTO ToDto(){ return new PersonDTO(); }
}

为什么你不只是像这样返回DTO:

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

public partial class PersonEntity : CommonEntityBase {
    // changed PersonDTO to CommonDTOBase
    public override CommonDTOBase ToDto(){ return new PersonDTO(); }
}

我认为这对于OO代码来说更为惯用。您是否有理由知道DTO的确切类型?

答案 1 :(得分:0)

我不知道LLBLGen,但我相信你可以通过引入一个接口来保存类型参数来解决你的问题:

public interface DTOProvider<T> where T : CommonDTOBase {
    public T ToDTO();
}

然后对于您的实体类,请执行以下操作:

public partial class PersonEntity : CommonEntityBase, DTOProvider<PersonDTO> {
    public PersonDTO ToDto() { return new PersonDTO(); }
}

因为部分类可以引入不同的接口,所以这是有效的。唯一的悲哀是需要强制转换才能通过基类型访问该方法:

public void DoSomethingWithDTO<T>(CommonBaseEntity entity)
        where T : CommonDTOBase {
    T dto = ((DTOProvider<T>) entity).ToDTO();
    ...
}

当然,如果你有一个实体派生类型的引用,你可以在没有演员的情况下直接调用ToDTO:

public void DoSomethingWithPersonDTO(PersonEntity entity)
{
    PersonDTO dto = entity.ToDTO();
    ...
}

如果您使用的是.NET Framework 4,则可以使用泛型差异通过声明DTO类型协变来使代码更容易使用DTOProvider接口,而这些代码只关心使用CommonDTOBase:

public interface DTOProvider<out T> where T : CommonDTOBase {
    public T ToDTO();
}

(注意'out'。)然后你的DoSomethingWithDTO方法不需要type参数:

public void DoSomethingWithDTO(CommonBaseEntity entity) {
    CommonDTOBase dto = ((DTOProvider<CommonDTOBase>) entity).ToDTO();
    ...
}

尝试在CommonBaseEntity分部类上声明: CommonBaseEntity, DTOProvider<T>很有诱惑力。不幸的是,这不起作用,因为当合并部分定义时,类型参数被转移,并且您的CommonBaseEntity类型最终成为泛型类型,它看起来就是让您首先进入绑定的原因。