2013-10-08 182 views
2

我正在构建一个使用物理属性测量的应用程序。起初,我用了一大堆的类型“双”的变量,并在匈牙利命名法或在我的头上不停值的轨迹:测试通用类型实现接口并使用该接口

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)。我认为这可能与我的问题有关。

回答

1

我主要是跳过这里的泛型。你想要做的是为你的每个单元创建类型安全的容器:毫米,毫安等。你希望在毫米级的计算中不可能使用毫米。如果您的集合是通用值,则此错误仍可能发生。

我会做的还是为您的措施定义一个通用接口。但包括价值应该是界面的一部分。我仍然会实现毫米和毫米等特定类型。这些类型也可能有隐式或显式的内置转换。但是,我会使用已经包含在.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 

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

+0

这似乎是明智的。关于你的最终评论,是不是一个完全抽象的基类会要求你重新实现这些方法?看来我们真的想要一个完整的基类。 – kvermeer

+0

@kvermeer标记类抽象(VB:MustInherit)只是意味着您必须从中继承,并且无法直接创建实例。您可以在MustInherit类型中使用具体的方法。 –

+0

啊,这很有道理!谢谢。似乎现在工作顺利.... – kvermeer

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.价值is used all over the place and you are bound to run into a naming conflict eventually. Perhaps测量or MeasuredValue`,而不是一个更好的名字。

+0

谢谢,我错过了这种语法。但是,GetType仍然返回System.Type。我是否需要在我的Value(或Measure或MeasuredValue)类中重新实现这个以返回IGenericUnits? – kvermeer

+0

在将System.RuntimeType投射到我的IGenericUnits类型后遇到更多麻烦,我决定使用Joel的答案并完全跳过泛型,但感谢您的帮助! – kvermeer

+0

为了子孙后代的修改而回答了您的问题。 – tcarvin