Как передать атрибут через реализацию интерфейса в .Net - PullRequest
0 голосов
/ 06 февраля 2019

Я работаю в winform на .Net, который генерирует элементы управления автоматически на основе класса модели, используя 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

Итак, на самом деле существует простой способ передать атрибут черезреализация интерфейса?

1 Ответ

0 голосов
/ 07 февраля 2019

Вам следует запросить определения 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

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)

Рабочий со свойством 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...