开发者

Refactoring to get inheritance/interfaces/convariance correct

开发者 https://www.devze.com 2023-03-27 04:18 出处:网络
EDIT:I\'ve added the C# equivilents to appeal to a wider audience - hope that\'s ok VS2008 - .Net 3.5 (I therefore don\'t get convariance/contravariance support, as far as I know?)

EDIT: I've added the C# equivilents to appeal to a wider audience - hope that's ok

VS2008 - .Net 3.5 (I therefore don't get convariance/contravariance support, as far as I know?)

It seems this is likely to require convariance/contravariance and therefore .Net 4, so to prevent the question being impossible, feel free to answer for .Net4 though, I would prefer a simple refactoring if it's available.

This should be simple but for some reason my brain just isn't up to the (any?) challenge this morning.

I have 2 applications (A & B). Details of application A hopefully aren'y important, so the following is WRT AppB which has 3 projects, all class libraries:

  1. Exposes my Domain Entities to the rest of AppB, and is also exposed to AppA
  2. Exposes my Data Model to AppB only (uses L2S, normal context and entities)
  3. Exposes functionality to AppA, using the Domain Entities for parameters/return types

I have the following interfaces in the 3 different projects:

In Project 1 (Domain Entities)

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

In Project 2 (Data Model)

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>
{
}

In Project 3 (Functi开发者_如何学编程onality)

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

The idea was that the functionality contracts (in Project 3) would only use Domain Entities (in Project 1) as arguments/returns, because only Projects 1 and 3 are exposed to Application A, Project 2 is not.

My L2S entities are extended to implement the Project 2 Interface. There are actually several additional methods on Project 2's contract but hopefully not relevant.

Until I actually typed it, for some reason I had it in my head that I would get away with the following implementation of Project 3's contract:

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
    }
}

This worked in my head for a while because Project 2's contract inherits Project 1's, but this of course is not the case because I have narrowed the constraint which therefore cannot implement the corresponding project 3 interface.

The question is, how would I go about achieving what I sought:

  • Project 2 should remain invisible to Application A, Project 1 and 3 however are exposed and should describe the functionality available and the entities to which Application A can/must use in order to call Application B
  • The Implementation of Project 3's contract, ideally, constrains the parameters to actually being the entities declared in the hidden project 2.

I've got a feeling I'm asking the impossible, but I'm hoping someone outside the box looking in can suggest something I can change to achieve, or come close to achieving my goals?

Thanks in advance!!

EDIT:

I knew the question wouldn't be clear because what I've described in the question is simply how to do something wrong...

Here's more details on the implementation of Project 3's contract which hopefully makes things more clear (bearing in mind that Project 2's contract is only implemented by auto-generated Linq Entities):

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
    }
}


What happens if you declare you interfaces like this, this would require .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

Would this make the inheritors of the interfaces covariant with the parent types and enable the code to run?

0

精彩评论

暂无评论...
验证码 换一张
取 消