【问题标题】:C# derivative generic type argumentsC# 派生泛型类型参数
【发布时间】:2013-11-11 12:11:17
【问题描述】:

我正在使用 ASP.NET MVC 4、Entity Framework 和 C# 制作一个 Web 应用程序,并且我正在编写抽象超类来封装实体模型和视图模型。虽然细节不是那么重要,但我的问题是我希望这些抽象类实现从任何给定视图模型映射到相应实体模型的函数,反之亦然。

我实际上已经使用泛型和反射实现了这样的方法,但是我想让它更整洁。我通过这样定义 EntityModel 类来完成所有工作:

public abstract class EntityModel
{
    public TVM MapToViewModel<TVM, TEM>()
        where TVM : ViewModel<TEM>, new()
        where TEM : EntityModel, new()
    { (...) }
}

似乎没有必要将实体模型的类型作为参数发送,因为调用对象会知道它自己的类型并让调用代码指定它会导致愚蠢的错误,但我不知道如何摆脱它。将方法定义为

public TVM MapToViewModel<TVM>()
    where TVM : ViewModel<EntityModel>, new()

看起来更整洁,但由于 EntityModel 是抽象的,因此会产生编译时错误。有没有办法告诉编译器它必须是 EntityModel 的派生而不是 EntityModel 本身?还是有其他更好的解决方案?

ViewModel 类非常相似,定义为:

public abstract class ViewModel<T>
    where T : EntityModel, new()

它正在按预期工作。

【问题讨论】:

  • 您是否使用接口而不是抽象基类获得任何好处?
  • 我看不出接口有什么帮助,因为我想直接在父类中实际实现映射方法,而不是单独为每个子类实现。除此之外,我还有同样的问题,不是吗?
  • 并行层次结构在 OO 语言中处理得不是特别好。此外,C# 缺乏虚拟方法的返回类型协方差。我不知道解决这个问题的好方法。考虑采用不太通用的方法。

标签: c# reflection generic-constraints


【解决方案1】:

考虑将映射功能移到实体和视图模型类之外。这将导致更适当的关注点分离,并消除您当前的通用签名问题。例如:

public abstract class EntityModel
{
}

public abstract class ViewModel<T>
    where T : EntityModel
{
}

public class ModelMapper<TEM, TVM>
    where TEM : EntityModel, new()
    where TVM : ViewModel<TEM>, new()
{
    public virtual TVM MapToViewModel(TEM entityModel)
    {
        // Default implementation using reflection.
    }

    public virtual TEM MapToEntityModel(TVM viewModel)
    {
        // Default implementation using reflection.
    }
}

【讨论】:

  • 我可以看到的主要问题是,您实际上可以将 User 类型的实体对象映射到另一个实体模型的视图模型,这可能会导致愚蠢的错误。我想我也许可以在映射方法的实际实现中包括检查这些东西。此外,感觉空类通常是个坏主意,就像标记接口通常被认为是不好的做法一样。
  • 这怎么可能? TVM 在 ModelMapper 声明中被限制为 ViewModel。愚蠢的错误将在 ViewModel 级别,而不是映射器级别。另外,我不确定您认为在这种方法下哪些类是空的。
【解决方案2】:

Nicole 打败了我……只是想你可以用 FromEntity 代替,即:

public abstract class ViewModel<T>
    where T : EntityModel, new()
{
    public static ViewModel<T> FromEntity(T entity)
    {
        throw new NotImplementedException();
    }
}

public abstract class EntityModel
{
    //... properties, methods etc...
}

或者甚至让 ViewModel 在构造函数中使用 EntityModel

编辑

根据您的评论 - 是的,您是对的,我已将参数更改为 T 而不是 EntityModel。

这样做的好处是依赖来自 ViewModel > EntityModel,这应该是真正的方式:)

【讨论】:

  • 这个我喜欢!只有您应该将 FromEntity 参数 e 更改为类型 T 以使该方法更安全。我想我可能会用这个运行,我会玩一段时间。
猜你喜欢
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 2014-03-22
  • 2019-04-15
相关资源
最近更新 更多