如何通过.Net上的接口实现传递属性

时间:2019-02-06 17:45:30

标签: .net vb.net generics interface attributes

我正在.net上的winform中工作,该人使用System.Reflection根据模型类自动生成控件,如下所示:

Imports System.Reflection

Public Class DynamicForm(Of Model As {IModelBase, Class, New})
    Protected Sub InitializeComponent()
        'A lot of not relevant code here...
        For Each prop As PropertyInfo In GetType(Model).GetProperties
            If Not HasControl(prop) Then Continue For
            Dim control = CreateControl(prop)
            Dim label = CreateLabel(prop)
            SetLocation(label, control)
            Me.Controls.Add(control)
            Me.Controls.Add(label)
            ResizeWindow(control.Height)
        Next
    End Sub
End Class

当然,模型可以具有很多我不希望用户直接编辑的属性,因此我采用了使用属性来标记要显示的属性的解决方案:

Public Class HasControl
    Inherits Attribute

    Public Property Label As String

    Public Sub New(label As String)
        Me.Label = label
    End Sub
End Class

然后我的模型如下:

Public Class Clients
    Implements IModelBase

    <HasControl("First name:")>
    Public Property FirstName As String

    Public Property CreatedTime As Date
End Class

现在,我使用IModelBase接口只是为了确保在此窗口中不能使用任何类,但最近我开始将其用于其他用途。我的许多模型都类似于客户,工人,卖方,用户等。这些模型具有许多相似的属性,因此我使用以下属性创建了一个界面:

Public Interface IHumanData
    Inherits IModelBase

    <HasControl("First name:")>
    Public Property FirstName As String

    <HasControl("Last name:")>
    Public Property LastName As String
End Interface

Public Class Seller
    Implements IHumanData

    Public Property FirstName As String Implements IHumanData.FirstName

    Public Property LastName As String Implements IHumanData.LastName
End Class

此问题是“ HasControl”属性未分配给我的卖方属性。我知道如果我使用继承而不是接口实现可以正常工作,但是当我要创建类似这样的东西时,继承有一个局限性:

Public Class Worker
    Implements IHumanData, IDateStorageData, ILocationData, ISortOfRandomData

    ' A bunch of properties auto generated by VisualStudio
End Class

那么,实际上有一种通过接口实现传递Attribute的简单方法吗?

1 个答案:

答案 0 :(得分:1)

您应该在interface定义中查询实现HasControl attribute的属性。
为此,您检索由表单中使用的对象实例实现的interfaces

构建表单的例程如下所示。

Sub buildForm(model As IModelBase)
    Dim interfaceTypes As Type() = model.GetType().FindInterfaces(New TypeFilter(Function(t, c) True), Nothing)

    For Each interfaceType As Type In interfaceTypes
        Dim properties As PropertyInfo() = interfaceType.GetProperties()
        For Each prop As PropertyInfo In properties
            Dim attributes As IEnumerable(Of HasControlAttribute) = prop.GetCustomAttributes(Of HasControlAttribute)()
            For Each attribute As HasControlAttribute In attributes
                Console.Write(attribute.Name)
                Console.Write(": ")
                Console.WriteLine(CStr(prop.GetValue(model, Nothing)))
                ' Build label and control here ... 
            Next
        Next
    Next
End Sub

您还可以从通用类型参数(以interfaces的形式)中检索这些Model

Dim interfaceTypes As Type() = GetType(Model).FindInterfaces(New TypeFilter(Function(t, c) True), Nothing)

通过将表单中正在使用的对象实例传递给它来调用它。 Worker实例。

Dim worker As Worker = New Worker()
worker.FirstName = "John"
worker.LastName = "Doe"
worker.City = "Brussels"
worker.Age = 40
buildForm(worker)

只考虑标记属性。
请注意,属性Age(如在Worker上定义的示例未出现。

First name: John
Last name: Doe
City: Brussels

IModelBase

Public Interface IModelBase
    ' ...
End Interface

具有Control属性

Public Class HasControlAttribute
    Inherits Attribute

    Public Sub New(ByVal name As String)
        Me.Name = name
    End Sub

    Public Property Name As String
End Class

IHumanData

Public Interface IHumanData
    Inherits IModelBase

    <HasControl("First name")>
    Property FirstName As String
    <HasControl("Last name")>
    Property LastName As String
    '... 
End Interface

ILocationData

Public Interface ILocationData
    <HasControl("City")>
    Property City As String
    ' ... 
End Interface

ISortOfRandomData

Public Interface ISortOfRandomData
    '...  
End Interface

工人

Public Class Worker
    Implements IHumanData, ILocationData

    Public Property FirstName As String Implements IHumanData.FirstName
    Public Property LastName As String Implements IHumanData.LastName
    Public Property City As String Implements ILocationData.City
    Public Property Age As Integer
    ' ...
End Class

更新

跟进您的评论。

如果您要包含Worker类本身定义的属性(未在任何已实现的接口中定义),如下所示, 那么interfaceTypes数组需要包含对象实例本身的Type

Dim interfaceTypes As IList(Of Type) = model.GetType().FindInterfaces(New TypeFilter(Function(t, c) True), Nothing).ToList()
interfaceTypes.Add(model.GetType)

工人,其属性Age标记为HasControl

Public Class Worker
    Implements IHumanData, ILocationData

    Public Property FirstName As String Implements IHumanData.FirstName
    Public Property LastName As String Implements IHumanData.LastName
    Public Property City As String Implements ILocationData.City

    <HasControl("Age")>
    Public Property Age As Integer
    ' ...
End Class