2

我正在构建一个使用物理特性测量的应用程序。最初,我使用了一大堆“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

但是随着我添加更多的项目和更多的属性,这很快就变得一团糟,到处都是转换因子,无法知道一个值是米还是毫安,没有硬编码就无法知道一个项目的正确缩写是什么和手动跟踪,以及添加选项以 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)。我认为这可能与我的问题有关。

4

2 回答 2

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. 重命名GenericUnitsIGenericUnits遵循推荐的命名约定

  2. 找到比Value" for your measurement class.Value is used all over the place and you are bound to run into a naming conflict eventually. PerhapsMeasurement orMeasuredValue` 更好的名称。

于 2013-10-08T14:21:04.937 回答
1

我大多会在这里跳过泛型。您想要做的重点是为每个单位创建类型安全的容器:毫米、毫安等。您希望在需要毫安的计算中无法使用毫米。如果你有通用值的集合,这个错误仍然可能发生。

我要做的仍然是为您的度量定义一个通用接口。但包括 Value 应该是接口的一部分。我仍然会为毫安和毫米之类的东西实现一个特定的类型。这些类型也可能具有内置的隐式或显式转换。但是我会使用 .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

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

于 2013-10-08T14:45:12.303 回答