0

TL;博士:

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


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

我开始从事的一项任务(并取得了良好的进展)是开发一个模板,该模板为每个实体生成一个 DTO。按照这些思路,一个目标是为我的实体提供一种ToDTO()方法。为了通用编程,我决定在一个公共基类中定义这个方法,这就是我的麻烦开始的地方。


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


LLBLGenCommonEntityBase像这样定义它的类:

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

我错了


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

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

当然,这是不可能的,因为据我所知

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


我要尝试的第三件事是简单地用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. 使用这种方法,执行以下操作:

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

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


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


这一切都是为了问我想要完成的事情是否可行。

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

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


编辑

这是我想用Porges 的方法完成的实体的用例:

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();
    }
}
4

2 回答 2

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 的确切类型?

于 2010-06-27T03:33:39.253 回答
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 方法不需要类型参数:

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

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

于 2010-06-26T23:35:49.320 回答