【问题标题】:Refactoring to get inheritance/interfaces/convariance correct重构以获得正确的继承/接口/协方差
【发布时间】:2011-10-23 21:51:52
【问题描述】:

编辑:我添加了 C# 等效项以吸引更广泛的受众 - 希望没关系

VS2008 - .Net 3.5(据我所知,我没有获得协变/逆变支持?)

似乎这可能需要协变/逆变,因此需要 .Net 4,所以为了防止这个问题是不可能的,尽管请随意回答 .Net4,如果它可用的话,我更喜欢简单的重构。

这应该很简单,但由于某种原因,我的大脑无法应对今天早上的(任何?)挑战。

我有 2 个应用程序(A 和 B)。应用程序 A 的详细信息希望并不重要,因此以下是 WRT AppB,它有 3 个项目,所有类库:

  1. 将我的域实体暴露给 AppB 的其余部分,同时也暴露给 AppA
  2. 仅将我的数据模型公开给 AppB(使用 L2S、正常上下文和实体)
  3. 向 AppA 公开功能,使用域实体作为参数/返回类型

我在 3 个不同的项目中有以下接口:

在项目 1(域实体)中

VB:

Public Interface IAppB_Project1_Contract(Of TKey)
     ReadOnly Property UniqueReference As TKey
End Interface

C#

public interface IAppB_Project1_Contract<TKey>
{
    TKey UniqueReference { get; }
}

在项目 2(数据模型)中

VB:

Public Interface IAppB_Project2_Contract(Of TKey)
     Inherits IAppB_Project1_Contract(Of TKey)
End Interface

C#:

public interface IAppB_Project2_Contract<TKey> : IAppB_Project1_Contract<TKey>
{
}

在项目 3(功能)中

VB:

Public Interface IAppB_Project3_Contract
     Function Create(Of T As {Class, IAppB_Project1_Contract(Of TKey)}, TKey)(ByVal entity As T) As TKey
End Interface

C#:

public interface IAppB_Project3_Contract
{
    TKey Create<T, TKey>(T entity) where T : class, IAppB_Project1_Contract<TKey>;
}

这个想法是,功能合同(在项目 3 中)将仅使用域实体(在项目 1 中)作为参数/返回,因为只有项目 1 和 3 暴露给应用程序 A,而项目 2 没有。

我的 L2S 实体已扩展为实现 Project 2 接口。实际上,项目 2 的合同中还有其他几种方法,但希望不相关。

直到我真正输入它之前,出于某种原因,我一直认为我会通过以下项目 3 合同的实现来逃脱:

VB:

Public Class Project3_Implementation
    Implements IAppB_Project3_Contract

    Public Function Create(Of T As {Class, IApp_Project2_Contract(Of TKey)}, TKey)(ByVal entity As T) As TKey Implements IAppB_Project3_Contract.Create
        ' notice the very subtle change to *Project TWO's* contract in the generics' constraint
    End Function
End Class

C#:

public class Project3_Implementation : IAppB_Project3_Contract
{
    public TKey Create<T, TKey>(T entity) where T : class, IApp_Project2_Contract<TKey>
    {
        // notice the very subtle change to *Project TWO's* contract in the generics' constraint
    }
}

这在我脑海中工作了一段时间,因为项目 2 的合同继承了项目 1 的合同,但当然不是这样,因为我已经缩小约束,因此无法实现相应的项目 3 接口。

问题是,我将如何实现我所追求的目标:

  • 项目 2 应该对应用程序 A 保持不可见,但项目 1 和 3 是公开的,并且应该描述可用的功能以及应用程序 A 可以/必须使用哪些实体才能调用应用程序 B
  • 理想情况下,项目 3 合同的实施将参数限制为实际上是隐藏项目 2 中声明的实体。

我有一种感觉,我在问不可能的事情,但我希望有人能跳出框框来提出一些建议,我可以改变以实现或接近实现我的目标?

提前致谢!!

编辑:

我知道这个问题并不清楚,因为我在问题中描述的只是如何做错事...

这里有更多关于项目 3 合同实施的详细信息,希望能让事情更清楚(请记住项目 2 的合同仅由自动生成的 Linq 实体实施):

VB:

Public Class MyImplementation
    Inherits System.Data.Linq.DataContext
    Implements IAppB_Project3_Contract

    Public Function Create(Of T As {Class, IApp_Project2_Contract(Of TKey)}, TKey)(ByVal entity As T) As TKey Implements IAppB_Project3_Contract.Create
        Me.GetTable(Of T).InsertOnSubmit(entity)
        Return entity.UniqueReference        ' the entity will implement the IApp_Project2_Contract by exposings it primary key - Return Me.MyPrimaryKeyField
    End Function
End Class

C#:

public class MyImplementation : System.Data.Linq.DataContext, IAppB_Project3_Contract
{
    public TKey Create<T, TKey>(T entity) where T : class, IApp_Project2_Contract<TKey>
    {
        this.GetTable<T>.InsertOnSubmit(entity);
        return entity.UniqueReference;
        // the entity will implement the IApp_Project2_Contract by exposings it primary key - Return Me.MyPrimaryKeyField
    }
}

【问题讨论】:

  • 为什么要为每个项目添加限制?我根本不明白为什么你需要定义这样的键?钥匙只是您的解决方案,我想知道您要解决的原始问题。如果我不了解实际问题,则无法正确回答。
  • 我知道这个问题有点混乱,请随时提出清理它的方法并使其更清晰。干杯!
  • @jguaffin - 不确定你指的是哪一部分?项目限制?我不希望应用程序 A 通过项目 2 的上下文直接联系数据库,数据非常敏感,需要通过项目 3 进行大量验证、转换等。关于键,我不能重载类 - 有时是表/entity 键是整数,有时是 Short,有时是 Long,有时是 GUID,等等……所以我使用了泛型和合同。我将添加一个快速的 sn-p 以显示我在 Project 3 实施中尝试做的事情...
  • 这不是一个协变/逆变问题吗?
  • 不要暴露数据库或数据库对象。添加一个执行所有验证的公共域层。限制数据库,以便只有正确的项目可以访问它(使用它自己的凭据)

标签: c# vb.net inheritance interface


【解决方案1】:

如果你声明这样的接口会发生什么,这需要 .Net 4.0+

Public Interface IAppB_Project1_Contract(Of Out TKey)
    ReadOnly Property UniqueReference As TKey 
End Interface

Public Interface IAppB_Project2_Contract(Of Out TKey)
    Inherits IAppB_Project1_Contract(Of TKey)
End Interface

Public Interface IAppB_Project3_Contract
    Function Create(Of T As {Class, IAppB_Project1_Contract(Of TKey)}, 
                    TKey)(ByVal entity As T) As TKey       
End Interface

这会使接口的继承者与父类型协变并使代码能够运行吗?

【讨论】:

  • 对于 Create 函数:错误 - 关键字“Out”和“In”只能在接口和委托声明中使用。
  • @Smudge202,我已经进行了相应的修改。有兴趣知道这个想法是否有效。
  • 感谢您迄今为止的帮助!不幸的是,编译器与我在开始时遇到的问题相同:'Public Function Create(Of T As {Class, IAppB_Project2_Contract(Of TKey)}, TKey)(entity As T) As TKey' cannot implement 'IAppB_Project3_Contract.Function Create(Of T As {Class, IAppB_Project1_Contract(Of TKey)}, TKey)(entity As T) As TKey' because they differ by type parameter constraints. 如果我的 contract3 实现只使用 contract1 作为通用约束,它当然可以在没有任何逆变的情况下工作,但它们必须是一种方式约束到contract2 ?? =(
  • +1 寻求帮助 - 完全忽略了协变性,自己设法回答了这个问题,稍后会发布。
猜你喜欢
  • 2023-04-02
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2017-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多