简体   繁体   中英

Using Type Inference with fluent interfaces

I have a class /interface hierarchy. On the interface side I have

IQuery
  ISelect      (inherits IQuery)
  IUpdate      (inherits IQuery)
  etc

On the class side I have

QueryBase       (implements IQuery)
  SelectQuery   (implements ISelect)
  UpdateQuery   (implements IUpdate)
  etc

Obviously, for example, both Update and Select classes share a WHERE clause but only a Select has GROUP BY functionality so ideally if an Update Query is being creating, the fluent interface will not give access to GROUP BY functionality but would do if a SelectQuery was being created.

eg in fluent interface terms

  var/Dim select = New SelectQuery()        <- returns ISelect explicit
                          .AddColumn(....)  <- returns ISelect explicit
                          .AddWhere(....)   <- returns ISelect inferred
                          .AddGroupBy(....) <- returns ISelect explicit

  var/Dim update = New UpdateQuery()        <- returns IUpdate explicit
                          .AddSet(....)     <- returns IUpdate explicit
                          .AddWhere(....)   <- returns IUpdate inferred

I am unsure how to implement the AddWhere function.

Previously I had declared the AddWhere function in the IQuery interface as

Function AddWhere(ByVal condition As ICriterion) As IQuery

IQuery AddWhere(ICriterion condition)

but because it was returning an IQuery, I was losing the benefits of the type inference and so as soon as the fluent interface had cast to the IQuery, if it was a Select query being created, I would no longer have access to, eg, the AddGroupBy method.

So I have tried to implement it as an Extension Method with generics

<Extension>
Public Function AddWhere(Of T As IQuery)(Byval this as T, Byval condition as Condition) as T
    this.SetWhere(condition)
    Return Me
End Function

public T AddWhere<T>(T @this, Condition condition) where T : IQuery
{
    @this.SetWhere(condition);
    return this;
}

with a Friend (internal) method, SetWhere, on QueryBase to permit me to update the WHERE clause. However because the generic is constrained to IQuery, it won't find the SetWhere. However, if I constrain as QueryBase, then, obviously, the compiler throws wobblies saying that the ISelect can't find an AddWhere method.

I'm thinking that I haven't quite got the inheritence chain or interface implementations quite right for what I'm trying to achieve.

(I hope that is clear!!)

I'd be grateful if someone could suggest either where I am going wrong in terms of the extension method implementation, or how I should better structure my class/interface hierarchy.

Public Interface IQuery
    Function AddWhere() As IQuery
End Interface

Public Interface IUpdate : Inherits IQuery
    Overloads Function AddWhere() As IUpdate
End Interface

Public Interface ISelect : Inherits IQuery
    Overloads Function AddWhere() As ISelect
    Function AddGroupBy() As ISelect
End Interface

Public Class QueryBase : Implements IQuery
    Public Function AddWhere() As IQuery Implements IQuery.AddWhere
        ''...
        Return Me
    End Function
End Class

Public Class UpdateQuery : Inherits QueryBase : Implements IUpdate
    Public Shadows Function AddWhere() As IUpdate Implements IUpdate.AddWhere
        MyBase.AddWhere()
        Return Me
    End Function
End Class

Public Class SelectQuery : Inherits QueryBase : Implements ISelect
    Public Shadows Function AddWhere() As ISelect Implements ISelect.AddWhere
        MyBase.AddWhere()
        Return Me
    End Function
    Public Function AddGroupBy() As ISelect Implements ISelect.AddGroupBy
        ''...
        Return Me
    End Function
End Class

Perhaps you could use another interface in your hierarchy, something like:

interface IQuery

interface IConditional : IQuery

interface ISelect : IConditional

interface IUpdate : IConditional

The IConditional interface could then have the AddWhere method, either directly in the interface definition or as an extension method constrained on the IConditional type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM