简体   繁体   English

测试该通用类型实现一个接口并使用该接口

[英]Testing that generic type implements an interface and using that interface

I'm building an app that uses measurements of physical properties. 我正在构建一个使用物理性能测量的应用程序。 Initially, I used a whole bunch of variables of type 'double' and kept track of the values in Hungarian notation or in my head: 最初,我使用了一堆类型为'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

but as I added more items and more properties this quickly became a mess, with conversion factors sprinkled about, no way of knowing if a value was in meters or millamps, no way of knowing what the proper abbreviation for an item was without hard-coding and manual tracing, and the idea of adding an option to input and output data in SI or Imperial units was terrifying. 但是随着我添加更多的项目和更多的属性,很快就陷入了混乱,转换因子泛滥,无法知道某个值是以米还是毫安为单位,也无法知道没有硬编码的情况下该项目的正确缩写是什么和手动跟踪,以及在SI或Imperial单位中添加用于输入和输出数据的选项的想法令人恐惧。

I realized that this problem was a typing problem, and that I could use a class with a type and a value to improve this arrangement: 我意识到这个问题是打字问题,我可以使用带有类型和值的类来改善这种安排:

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

so my declaration became: 所以我的声明变成了:

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

which is really nice and clean. 这真的很干净。 Now I want to use the properties and conversions defined by my operators, but I can't: 现在,我想使用操作员定义的属性和转换,但不能:

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())

How can I access the properties and methods of my type declaration? 如何访问类型声明的属性和方法?

I have found that my declaration Value(Of GenericUnits) doesn't actually reference the interface called GenericUnits , and instead produces a generic type; 我发现我的声明Value(Of GenericUnits)实际上没有引用名为GenericUnits的接口,而是生成了一个泛型类型。 I might as well call it Value(Of T) . 我不妨称之为Value(Of T) I think this might be related to my problem. 我认为这可能与我的问题有关。

You need to constrain your generic to derive from your interface: 您需要限制泛型从接口派生:

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

See here for addition info on generics and constraints: http://msdn.microsoft.com/en-us/library/w256ka79.aspx 有关泛型和约束的其他信息,请参见此处: http : //msdn.microsoft.com/zh-cn/library/w256ka79.aspx


Edit: 编辑:

Usage would be as follows: 用法如下:

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

A couple of recommendations: 一些建议:

  1. Rename GenericUnits to IGenericUnits to follow recommended naming conventions GenericUnits重命名为IGenericUnits以遵循建议的命名约定

  2. Find a better name than Value" for your measurement class. Value is used all over the place and you are bound to run into a naming conflict eventually. Perhaps Measurement or MeasuredValue` instead. Value" for your measurement class.找到一个比“ Value" for your measurement class.更好的名称is used all over the place and you are bound to run into a naming conflict eventually. Perhaps Measurement or MeasuredValue`。

I'd mostly skip the generics here. 我通常会在这里跳过泛型。 The whole point of what you want to do is create type-safe containers for each of your units: millimeters, milliamps, etc. You want to make it impossible to use a millimeter in a calculation that expects a milliamp. 要做的全部工作是为每个单位创建类型安全的容器:毫米,毫安等。您希望在期望毫安的计算中不能使用毫米。 If you have collections that are generic Values, this mistake can still happen. 如果您具有通用值的集合,则此错误仍然可能发生。

What I would do is still define a common interface for your measures. 我要做的仍然是为您的度量定义一个通用接口。 But including the Value should be part of the interface. 但是包含Value应该是接口的一部分。 I'd still implement a specific type for things like milliamp and millimeter. 我仍然会为毫安和毫米等实现特定的类型。 These types might also have implicit or explicit conversions built in. But then I would use the existing generic collections and types already included with .Net, rather than building any new ones. 这些类型也可能内置了隐式或显式转换。但是,我将使用.Net中已包含的现有通用集合和类型,而不是构建任何新的集合和类型。 More important, when using the types, I would ask for the final, concrete types and implement the interface, and avoid asking for the interface itself: 更重要的是,在使用类型时,我会要求最终的具体类型并实现接口,而避免要求接口本身:

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

I might even be tempted to use a full abstract base class, rather than an interface, because it would allow me to avoid re-implementing the same value property over and over again. 我什至会尝试使用完整的抽象基类,而不是使用接口,因为这将使我避免一遍又一遍地重新实现相同的value属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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