测试泛型类型实现接口并使用该接口

时间:2013-10-08 13:58:45

标签: .net vb.net generics types

我正在构建一个使用物理属性测量的应用程序。最初,我使用了一大堆'double'类型的变量,并用匈牙利表示法或在我脑海中跟踪值:

Public Class ResultData
    Public Property position_in_mm_vs_time_in_ms as Double(,)
    Public Property initial_position_in_mm as Double
    Public Property final_position_in_mm as Double
    Public Property duration_in_ms as Double
    Public Property speed_in_mm_per_s as Double
End Class

但随着我添加了更多项目和更多属性,这很快就变得一团糟,转换因子洒在上面,无法知道某个值是以米为单位还是millamps,无法知道项目的正确缩写是什么硬编码和手动跟踪,以SI或英制单位添加输入和输出数据选项的想法是可怕的。

我意识到这个问题是打字问题,我可以使用类型和值的类来改善这种安排:

Namespace Measure
    Public Class Value(Of GenericUnits)
        Public Property Value As Double
    End Class
    Public Class ValuePoint(Of XUnits, YUnits)
        Public X As Value(Of XUnits)
        Public Y As Value(Of YUnits)
        Public Sub New(x As Value(Of XUnits), y As Value(Of YUnits))
            Me.X = x
            Me.Y = y
        End Sub
    End Class
    Public Class Units
        Public Interface GenericUnits
            ReadOnly Property Abbreviation As String
            ' Additional properties, operators, and conversion functions
        End Interface
        ' Additional unit types
    End Class
End Namespace

所以我的声明成了:

Public Class ResultData
    Public Property PositionData as List(of ValuePoint(of Units.Seconds, Units.Millimeters))
    Public Property InitialPosition as Value(of Units.Millimeters)
    Public Property FinalPosition as Value(of Units.Millimeters)
    Public Property Duration as Value(of Units.Milliseconds)
    Public Property Speed as Value(of Units.MillimetersPerSecond)
End Class

这真的很干净。现在我想使用我的运算符定义的属性和转换,但我不能:

Dim result As New ResultData()
Dim msg As New System.Text.StringBuilder()
msg.AppendLine("Speed units are abbreviated as: ")
msg.AppendLine(result.Speed.GetType().ToString() & "?")
msg.AppendLine(result.Speed.GetType().GenericTypeArguments(0).ToString() & "?")
' Produces error "Abbreviation is not a member of System.Type"
' Casting produces conversion error
'msg.AppendLine(result.Speed.GetType().GenericTypeArguments(0).Abbreviation & "?")
' Produces:
'   Speed units are abbreviated as:
'   Measure.Value`1[Measure.Units+MillimetersPerSecond]
'   Measure.Units+MillimetersPerSecond
MsgBox(msg.ToString())

如何访问我的类型声明的属性和方法?

我发现我的声明Value(Of GenericUnits)实际上没有引用名为GenericUnits的接口,而是生成泛型类型;我不妨称之为Value(Of T)。我认为这可能与我的问题有关。

2 个答案:

答案 0 :(得分:2)

您需要限制您的通用从您的界面派生:

Public Class Measurement(Of T as IGenericUnits) 'class name was Value before edit 
    Public Property Value As Double
    Public Property UOM As IGenericUnits 'UOM is a common abbreviation for unit-of-measure
End Class

请参阅此处了解有关泛型和约束的其他信息:http://msdn.microsoft.com/en-us/library/w256ka79.aspx


编辑:

用法如下:

msg.AppendLine(result.Speed.UOM.Abbreviation)

一些建议:

  1. GenericUnits重命名为IGenericUnits,以遵循建议的命名惯例

  2. 找到一个比Value" for your measurement class.更好的名称is used all over the place and you are bound to run into a naming conflict eventually. Perhaps衡量or MeasuredValue`而不是。

答案 1 :(得分:1)

我主要是跳过这里的仿制药。您想要做的就是为每个单元创建类型安全的容器:毫米,毫安等。您希望在计算期望毫安时使用毫米。如果您的集合是通用值,那么这个错误仍然可能发生。

我要做的仍然是为您的度量定义一个通用接口。但是包括Value应该是界面的一部分。我仍然会为milliamp和millimeter这样的东西实现特定的类型。这些类型也可能内置了隐式或显式转换。但是我会使用.Net中已包含的现有泛型集合和类型,而不是构建任何新的。更重要的是,在使用类型时,我会要求最终的具体类型并实现接口,并避免要求接口本身:

Public Interface IUnit
    ReadOnly Property Abbreviation As String
    Property Value As Double
End Interface

Public Class MilliAmps Implements IUnit
    Public ReadOnly Property Abbreviation As String Implements IUnit.Abbreviation
        Get
          Return "ma"
        End Get
    End Property

    Public Property Value As Double Implements IUnit.Value
End Class

Public Class Millimeters Implements IUnit
    Public ReadOnly Property Abbreviation As String Implements IUnit.Abbreviation
        Get
          Return "mm"
        End Get
    End Property

    Public Property Value As Double Implements IUnit.Value
End Class
Public Class Seconds ...
Public Class MillimetersPerSecond ...

Public Class ResultData
    Public Property PositionData As List(Of Tuple(Of Seconds, Millimeters))
    Public Property InitialPosition As Millimeters
    Public Property FinalPosition As Millimeters
    Public Property Duration As Milliseconds
    Public Property Speed As MillimetersPerSecond
End Class

我甚至可能想要使用完整的抽象基类,而不是接口,因为它可以让我避免一遍又一遍地重新实现相同的值属性。

相关问题