帮助允许其他开发人员自定义类

时间:2010-09-12 02:32:43

标签: vb.net class interface methods override

更新:添加了一些示例代码以帮助澄清。

嗨,感觉这应该不是那么复杂,但我想我只是不知道我正在尝试做什么的正确名称。我正在处理一个ASP.net项目。

这个概念非常简单: 我有一个库提供一些ecomm功能。 图书馆中的一个类包含计算税收的功能。 库中的一个类围绕购物车标题。 该类由Web项目使用。

将会有一个方法:carthead.SaveCart。 在SaveCart中,它需要调用tax.CalculateTax

我需要做的是找出一种方法来允许carthead.SavCart始终可以访问特定的函数,例如tax.CalculateTax(例如,该函数必须始终可供库使用)。

但是,我想允许任何人创建tax.CalculateTax方法的不同版本。

我尝试过使用可覆盖的基类做一些事情,但我发现即使覆盖了Web项目中的税基类,它只会在我打电话时调用税级的重写版本网络项目。 我无法将税级变成接口,因为当我这样做时,我无法定义tax.CalculateTax的结果列表(因为在这个例子中t是一个接口,而不是一个实际的类) - 这个列表必须由carthead.SaveCart方法使用。

因此,当您单步执行代码时,您会发现当Web项目调用carthead.SaveCart方法时,carthead.SaveCart方法无法访问来自Web项目的重写代码...并导致调用tax.CalculateTax方法的非重写版本。

我确定我错过了什么,但我甚至不确定我应该研究什么,或者我正在努力完成的正确名称究竟是什么。

实际上要将其简化,我需要能够覆盖一个方法,并从库中的非可覆盖方法调用该方法的重写版本。

有人能向我指出我搞砸了什么,或者我应该看什么?

更新: 添加了一些代码片段以帮助澄清:

以下是有问题的CartHeader类:

Public Class CartHeader

'省略了其他类的东西

Public Function UpdateCartStep2(ByVal CartNo As Long, ByVal Username As String, _
ByVal PmtMethod As String, ByVal ShipMethod As String, _
ByVal ShipComplete As Boolean, ByVal ShipCost As Double, _
ByVal ShipInstr As String, Optional ByVal TaxGroup As String = "", _
Optional ByVal PickupLoc As String = "", _
Optional ByVal FuelSurcharge As Double = 0, _
Optional ByVal Misc As String = "", _
    Optional ByVal TaxThisSomeTaxOrder As Boolean = False, _
    Optional ByVal ShipToID As Long = 0, _
    Optional ByVal ShipToZip As String = "", _
    Optional ByVal mCustCode As String = "", _
    Optional ByVal CustTax As Tax = Nothing) As Integer
    '=================>
    'note that the last parameter is new which is what we're currently using to pass in the customtax class so that we can consume it inside this method
    '==================>


    If IsNothing(CustTax) Then
        CustTax = New Tax
    End If

    '6-29-08 this stored proc was updated to allow for fuel surcharge
    'added fuel surcharge parameter

    Dim Resultval As Integer
    Dim strConnect As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString)
    Dim SqlCommand As New SqlCommand("sp_UpdateCartStep2", strConnect)

    Try

        Dim SubTotalAmt As Double
        SubTotalAmt = GetCartSubTotal(CartNo)

        GetCartHeader(CartNo)

        Dim CartTax As Double

        Dim SystemTypeID As Integer = CInt(ConfigurationManager.AppSettings("SystemTypeID").ToString)

        Select Case SystemTypeID
            Case 1

                If profile.AllowTerms = False Then
                    CartTax = CalcTax(SubTotalAmt, ShipCost, FuelSurcharge, m_Ship_State_Province)
                Else
                    CartTax = 0
                End If
            Case 2
                '6-29-08 added to figure fuel surcharge
                'Dim CustTax As New Tax
                'Dim CustCode As String = System.Web.HttpContext.Current.Profile("CustCode")
                Dim lCustTax As New List(Of Tax)

                '=========================>
                'note that this part of the header must always call into the calctax method.
                'it should be able to call the custom method if it has been defined.
                lCustTax = CustTax.wa_cc_CalcTax(mCustCode, ShipToID, SubTotalAmt, ShipCost, FuelSurcharge, CStr(m_Ship_State_Province), CStr(ShipToZip))
                '==========================>


                For Each ct As Tax In lCustTax
                    CartTax += ct.Tax
                Next
                'CartTax = CalcTax(SubTotalAmt, ShipCost, FuelSurcharge, m_Ship_State_Province, TaxGroup)

        End Select

        SqlCommand.CommandType = CommandType.StoredProcedure
        strConnect.Open()

        SqlCommand.Parameters.AddWithValue("@CartNo", SqlDbType.BigInt).Value = CartNo
        SqlCommand.Parameters.AddWithValue("@Username", SqlDbType.VarChar).Value = Username
        SqlCommand.Parameters.AddWithValue("@PmtMethod", SqlDbType.VarChar).Value = PmtMethod
        SqlCommand.Parameters.AddWithValue("@ShipMethod", SqlDbType.VarChar).Value = ShipMethod
        SqlCommand.Parameters.AddWithValue("@ShipCompleteFlag", SqlDbType.Bit).Value = ShipComplete
        SqlCommand.Parameters.AddWithValue("@ShipCost", SqlDbType.Money).Value = ShipCost
        SqlCommand.Parameters.AddWithValue("@Tax", SqlDbType.Money).Value = CartTax
        SqlCommand.Parameters.AddWithValue("@ShipInstr", SqlDbType.VarChar).Value = ShipInstr
        SqlCommand.Parameters.AddWithValue("@PickupLoc", SqlDbType.VarChar).Value = PickupLoc
        SqlCommand.Parameters.AddWithValue("@FuelSurcharge", SqlDbType.Float).Value = FuelSurcharge

        '1-30-08 Changed to accomodate holding the carrier number when selecting collect freight
        'required modification of the sp_UpdateCartStep2 stored procedure.
        SqlCommand.Parameters.AddWithValue("@Misc3", SqlDbType.VarChar).Value = Misc3

        SqlCommand.ExecuteNonQuery()
        Resultval = 0
    Catch ex As Exception
        Resultval = -1
        System.Web.HttpContext.Current.Trace.Write(ex.Message)
    Finally
        strConnect.Close()
    End Try

    Return Resultval
End Function

结束班


这是我们用作基类的类...如果基函数calcs不适用,它会覆盖wa_cc_calctax。

Public Class Tax


Private _Tax As Double
Public Property Tax() As Double
    Get
        Return _Tax
    End Get
    Set(ByVal value As Double)
        _Tax = value
    End Set
End Property

Private _TaxRate As Double
Public Property TaxRate() As Double
    Get
        Return _TaxRate
    End Get
    Set(ByVal value As Double)
        _TaxRate = value
    End Set
End Property


Private _TaxDesc As String
Public Property TaxDesc() As String
    Get
        Return _TaxDesc
    End Get
    Set(ByVal value As String)
        _TaxDesc = value
    End Set
End Property


Private _TaxGroupID As String
Public Property TaxGroupID() As String
    Get
        Return _TaxGroupID
    End Get
    Set(ByVal value As String)
        _TaxGroupID = value
    End Set
End Property


Private _TaxJurisdictionID As String
Public Property TaxJurisdictionID() As String
    Get
        Return _TaxJurisdictionID
    End Get
    Set(ByVal value As String)
        _TaxJurisdictionID = value
    End Set
End Property



Private _TaxCustCode As String
Public Property TaxCustCode() As String
    Get
        Return _TaxCustCode
    End Get
    Set(ByVal value As String)
        _TaxCustCode = value
    End Set
End Property


Private _TaxFreight As Boolean
Public Property taxFreight() As Boolean
    Get
        Return _TaxFreight
    End Get
    Set(ByVal value As Boolean)
        _TaxFreight = value
    End Set
End Property




Public Enum TaxableStatus
    All
    None
    some
End Enum


''' <summary>
''' It will first try to figure out if we're shipping to the same zip as the ship to
''' if it is the same, then we'll use the ship-tos tax group
''' if it is different, then we'll go to manual tax.
''' in manual tax, the customer record is reviewed and the class_1id field is interogated.
''' The code selected tells us what states the customer is taxable for.
''' If we are in those states, then the customer tax group is chosed based on the state.
''' </summary>
''' <param name="mCustCode"></param>
''' <param name="mShipToID"></param>
''' <param name="SubTotalAmt"></param>
''' <param name="FreightCost"></param>
''' <param name="FuelSurcharge"></param>
''' <param name="m_Ship_State_Province"></param>
''' <param name="m_Zip"></param>
''' <param name="TaxGroup"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Function wa_cc_CalcTax(ByVal mCustCode As String, _
                       ByVal mShipToID As String, _
                       ByVal SubTotalAmt As Double, _
                     ByVal FreightCost As Double, _
                    ByVal FuelSurcharge As Double, _
                    ByVal m_Ship_State_Province As String, _
                    ByVal m_Zip As String, _
                     Optional ByVal TaxGroup As String = "") As List(Of Tax)

    'do some 'normal' tax calcs.


    Return New List(Of Tax)

End Function

结束班


这是覆盖wa_cc_calctax函数的CustomTax类:

Public Class CustomTax

Inherits Tax


''' <summary>
''' It will first try to figure out if we're shipping to the same zip as the ship to
''' if it is the same, then we'll use the ship-tos tax group
''' if it is different, then we'll go to manual tax.
''' in manual tax, the customer record is reviewed and the class_1id field is interogated.
''' The code selected tells us what states the customer is taxable for.
''' If we are in those states, then the customer tax group is chosed based on the state.
''' </summary>
''' <param name="mCustCode"></param>
''' <param name="mShipToID"></param>
''' <param name="SubTotalAmt"></param>
''' <param name="FreightCost"></param>
''' <param name="FuelSurcharge"></param>
''' <param name="m_Ship_State_Province"></param>
''' <param name="m_Zip"></param>
''' <param name="TaxGroup"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overrides Function wa_cc_CalcTax(ByVal mCustCode As String, _
                       ByVal mShipToID As String, _
                       ByVal SubTotalAmt As Double, _
                     ByVal FreightCost As Double, _
                    ByVal FuelSurcharge As Double, _
                    ByVal m_Ship_State_Province As String, _
                    ByVal m_Zip As String, _
                     Optional ByVal TaxGroup As String = "") As List(Of Tax)
    Dim lTX As New List(Of Tax)

    Dim mUseP21Tax As Boolean = True
    If mShipToID <= 0 Then
        mUseP21Tax = False
    End If

    If FreightCost <= 0 Then
        FreightCost = 0
    End If

    Dim tx As New CustomTax
    Dim ZipMatch As Boolean
    If mShipToID > 0 Then
        'we're dealing with a selected ship to so we should see if it all still matches
        ZipMatch = CheckZipAgainstShipTo(m_Zip, mCustCode, mShipToID)
    Else
        'this item is not a selected ship-to so no need to look for a match
        ZipMatch = False
    End If

    If ZipMatch = True Then

        lTX = LookupTaxForShipTo(mCustCode, mShipToID, SubTotalAmt, FreightCost)
    Else
        lTX = LookupManualTax(mCustCode, m_Ship_State_Province, SubTotalAmt, FreightCost, , m_Zip)


    End If


    Return lTX

结束功能 结束班


所以问题在于: 1)如果我将类Tax作为接口或抽象类,那么我必须将其作为cartheader类中的新类'new',以便我可以调用wa_cc_lookupclass方法。 2)当我们在cartheader类中新建一个tax class时,我们不会新建tax类的自定义实例,因此不会使用自定义代码。

目标如下: 1)提供具有“正常”功能的基本Tax类。 2)允许用户覆盖Web应用程序的app_code文件夹中的tax类,以创建计算税的新逻辑。它仍将具有与原始相同的输入和输出签名。 3)cartheader.UpdateCartStep2方法必须能够从基类或重写的自定义类(如果被覆盖)访问wa_cc_calctax函数。

在此示例中,我们已经明确入侵了它,并将税务类(CustomTax)的自定义版本作为参数传递到UpdateCartStep2方法中。这是我们根据此处的建议实施的解决方法......但我们知道这不是“正确”的方式。

4 个答案:

答案 0 :(得分:1)

我可以想到两个选择。

  1. 将您的购物车类设为抽象类,并将CalculateTax方法设为抽象方法。这将迫使其他开发人员实现他们自己的CalculateTax方法并扩展您的基类。

  2. 将参数传递给SaveCart方法(或类构造函数),该方法提供有关如何计算税收的信息,例如代表或职能。然后SaveCart方法调用该委托来执行'CalculateTax'部分。

答案 1 :(得分:0)

如下所示:

abstract class BaseCartOperations
{
    public void SaveCart ()
    {

        // ...

        CalculateTax();
    }


    protected void CalculateTax ()
    {
        // Base Tax Stuff
        CalculateTaxInternal();
    }


    // Force implementation
    protected abstract void CalculateTaxInternal ();
}

? (或其中的一些变体)。

答案 2 :(得分:0)

重新阅读问题和评论后,我发现你想解决两件事:

  1. 如何强制调用基类方法
  2. 如何让第三方提供系统中涉及的课程
  3. 进一步回答上述问题,但我更愿意:

    定义一个ITaxCalculator(你可以找到一个更好的名字......)。在需要进行计算的方法中接收ITaxCalculator。提供DefaultTaxCalculator或类似的东西(如果适用),如果计算器参数为null,则让客户端调用通过您或将其用作默认值。

    如果您的代码库难以将其传递到流程中,请定义TaxCalculatorFactory,如下面的CartFactory,并且只执行TaxCalculatorFactory.Create()/,如果适用,默认情况下应将其设置为DefaultTaxCalculator。

    一旦有了ITaxCalculator的实例,就可以调用taxCalculator.CartTax(currentCart)来获得购物车税。

    这样,扩展点更加集中,最终使其变得更加简单。


    回答原始问题:

    对于1,您不能直接强制它。它的子类决定调用base.SameMethod(),它允许它在执行基本方法之前和/或之后放置一些逻辑。

    您可以通过在覆盖的方法中不要始终调用您想要的逻辑来解决此问题。你所做的是调用1或2个可以覆盖的方法,对于CartSave,你可以定义OnCartSaving和OnCartSaved。 CartSave不能被覆盖,但是当它调用那些可以被覆盖的2时,你提供了扩展点。

    对于2,我认为这是一个全新的问题,并且有各种方法可以解决它。

    如果您需要控制何时创建实例,您可以创建一个Factory,可以通过客户端代码(或通过应用程序的.config)进行配置以使用它们的类。就像框架对不同的数据提供者一样。

    一个简单的变体可能是使用Func公开静态属性,客户端代码可以设置为如下函数:

    CartFactory.Create =()=&gt;新的MySpecificCart();

    如果它的asp.net你可以在应用程序中将它放在global.asax中。然后,您需要创建购物车实例,您可以:CartFactory.Create();


    原始答案 - 忽略/我最初没有得到问题:

    只是一个猜测,但似乎您没有使用覆盖关键字在特定类中声明该方法。

    如果上述情况恰好如此,您可能会收到编译器的警告。

答案 3 :(得分:0)

在与Microsoft讨论(并收取费用)之后,他们确定在这种情况下唯一可以做的就是将新对象或谓词函数的实例传递给类。这是因为类被编译成dll,而在内部,dll将引用dll中定义的基类... 因此,我们创建了一个传递基类的重写实例的选项...让代码检查以查看实例中传递的是否为空,以及它是否是新基类。 如果它被提供,那么我们使用实例中传递的而不是新建一个基类。

希望这有助于其他人。