【问题标题】:How to pass an Attribute through an interface implementation on .Net如何通过.Net上的接口实现传递属性
【发布时间】:2019-06-30 18:15:21
【问题描述】:

我在 .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

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

【问题讨论】:

    标签: .net vb.net generics interface attributes


    【解决方案1】:

    您应该查询实现HasControl attribute 的属性的interface 定义。
    为此,您需要检索由表单中使用的对象实例实现的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
    

    您还可以从泛型类型参数(在您的表单Model)中检索这些interfaces

    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
    

    HasControlAttribute

    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)
    

    Worker,属性为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
    

    【讨论】:

    • 这几乎是我所需要的,如果我需要将“HasControl”属性添加到 Age 属性,它将被您的“interfaceTypes”数组忽略。
    • 在上面的例子中,我确实包含了Age 属性,以便显式处理。如果您希望考虑它,您是否可以在其中一个接口中定义它,例如。 IHumanData 并应用 HasControl 属性,cfr。例如。 FirstName?
    • 我更新了答案以包含 Worker 类本身的属性,具有 HasControl 属性。
    • 很好,这个更新添加了重复的属性,但只要我只是想评估它们是否具有“HasControl”属性,这个效果很好。
    猜你喜欢
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-05
    • 1970-01-01
    相关资源
    最近更新 更多