theskiguy Ответов: 3

Событие, определяющее, изменился ли ряд переменных


Я создаю программу, которая будет контролировать выход внешнего процесса. У меня есть настройка таймера, которая запрашивает данные от внешнего процесса каждый тик, и результаты хранятся в моем классе. Если какая-либо из переменных изменилась по сравнению с их предыдущим состоянием, я хочу обновить результаты в базе данных. Я настроил тестовую программу, которая имитирует это, но в гораздо меньшем масштабе. На данный момент я проверяю только 3 переменные. Я настроил класс, который содержит результаты 3 переменных, и я создал свойства для каждой переменной. В каждом свойстве я вызываю событие, которое срабатывает, когда переменная меняет значение.

Затем я создал обработчик событий, который обрабатывает все 3 события. Если срабатывает одно из событий, моя база данных обновляется. Все это отлично работает, за исключением одного. Если одна переменная была изменена, она прекрасно обновляет переменную в базе данных. Если 2 переменные изменяются одновременно, событие срабатывает дважды, что обновляет базу данных 2 раза. Если все 3 переменные изменятся одновременно, база данных обновится 3 раза. Я хочу избежать перегрузки базы данных, поэтому я не хочу обновлять ее чаще одного раза за каждый тик таймера, независимо от того, изменяется ли одна переменная или все переменные. Как с этим лучше всего справиться? Единственное, что я могу придумать, - это создать одну переменную и одно свойство, содержащее все 3 результата в виде массива, списка или чего-то подобного. Это лучший вариант или мне чего-то не хватает?

Что я уже пробовал:

Public Class ClassTimerVars

    Private _Feedrate As Integer
    Public Event FeedrateChanged()
    Private _Spindle As Integer
    Public Event SpindleChanged()
    Private _Rapid As Integer
    Public Event RapidChanged()
   


    Public Property Feedrate() As Integer
        Get
            Feedrate = _Feedrate
        End Get
        Set(ByVal value As Integer)
            If _Feedrate <> value Then
                _Feedrate = value
                RaiseEvent FeedrateChanged()
            End If
        End Set
    End Property

    Public Property Spindle() As Integer
        Get
            Spindle = _Spindle
        End Get
        Set(ByVal value As Integer)
            If _Spindle <> value Then
                _Spindle = value
                RaiseEvent SpindleChanged()
            End If
        End Set
    End Property

    Public Property Rapid() As Integer
        Get
            Rapid = _Rapid
        End Get
        Set(ByVal value As Integer)
            If _Rapid <> value Then
                _Rapid = value
                RaiseEvent RapidChanged()
            End If
        End Set
    End Property

End Class


Public Class Form1

    Private TimerTick As Long = 0
    Private WithEvents test As New ClassTimerVars
    Private EventsFired As Long = 0


    Private Sub OverrideChanged() Handles test.FeedrateChanged, test.SpindleChanged, test.RapidChanged

        Me.TxtFeedrate.Text = test.Feedrate
        Me.TxtSpindle.Text = test.Spindle
        Me.TxtRapid.Text = test.Rapid

        EventsFired += 1
        Me.LblEventsFired.Text = "Events Fired = " & EventsFired

        'Update the Database here

    End Sub

    Private Sub BtnStartTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStartTimer.Click
        Timer1.Start()
    End Sub

    Private Sub BtnStopTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStopTimer.Click
        Timer1.Stop()
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

        'Simulate Extracting data from an external process
        'Update the 3 variables on different intervals to see how many times the event is triggered

        TimerTick += 1

        'Update Feedrate in the class
        If TimerTick Mod 2 = 0 Then
            test.Feedrate = TimerTick
        End If

        If TimerTick Mod 4 = 0 Then
            test.Spindle = TimerTick
        End If

        If TimerTick Mod 8 = 0 Then
            test.Rapid = TimerTick
        End If


    End Sub
End Class

[no name]

Я думаю, что создал бы класс event args, который содержал бы все три значения.

Peter Leow

Создайте класс с 3 переменными в качестве свойств и реализуйте уведомление об изменении свойств https://msdn.microsoft.com/en-us/library/ms743695(v=против 110). aspx

theskiguy

Питер:

Я никогда раньше не использовал уведомление об изменении свойств. Можете ли вы сказать мне, в чем преимущество его использования по сравнению с тем, как я поднимаю события в своем классе?

3 Ответов

Рейтинг:
26

Maciej Los

Как NotPolitcallyCorrect[^] упомянутый в комментарии к вопросу, вы должны использовать Класс EventArgs (System)[^]. В нижней части соответствующей страницы вы найдете пример.

[РЕДАКТИРОВАТЬ]
Вот пример кода, который использует ваш класс:

'methods and classes

Public Sub ctv_DataHasBeenAdded(sender As Object, e As ClassTimerVarsEventArgs)
	Console.WriteLine("New data has been added! Feedrate: {0}, Spindle: {1}, Rapid: {2}.", e.Feedrate, e.Spindle, e.Rapid)
	Dim c As ClassTimerVars = DirectCast(sender, ClassTimerVars)
	Console.WriteLine(c.ToString())
End Sub

Public Class ClassTimerVars
    Private _Feedrate As Integer
    Private _Spindle As Integer
    Private _Rapid As Integer

  	Public Sub New()
		'default constructor	
	End Sub
	
	Public Sub New(iFeedrate As Integer, iSpindle As Integer, iRapid As Integer)
    	_Feedrate = iFeedrate
    	_Spindle = iSpindle
    	_Rapid = iRapid
    End Sub

    Public Property Feedrate() As Integer
        Get
            Feedrate = _Feedrate
        End Get
        Set(ByVal value As Integer)
            _Feedrate = value
        End Set
    End Property

    Public Property Spindle() As Integer
        Get
            Spindle = _Spindle
        End Get
        Set(ByVal value As Integer)
            _Spindle = value
        End Set
    End Property

    Public Property Rapid() As Integer
        Get
            Rapid = _Rapid
        End Get
        Set(ByVal value As Integer)
            _Rapid = value
        End Set
    End Property

	Public Overrides Function ToString() As String
		Return String.Format("ClassTimerVars totals - Feedrate: {0}, Spindle: {1}, Rapid: {2}.", _Feedrate, _Spindle, _Rapid)
	End Function

    Public Sub AddData(ByVal iFeedrate As Integer, ByVal iSpindle As Integer, ByVal iRapid As Integer)
            _Feedrate += iFeedrate
            _Spindle += iSpindle
			_Rapid += iRapid
			Dim args As ClassTimerVarsEventArgs = New ClassTimerVarsEventArgs() _
				With {.Feedrate = iFeedrate, .Spindle = iSpindle, .Rapid = iRapid}
            OnDataAdded(args)
    End Sub

    Protected Overridable Sub OnDataAdded(e As ClassTimerVarsEventArgs)
        RaiseEvent DataHasBeenAdded(Me, e)
    End Sub

    Public Event DataHasBeenAdded As EventHandler(Of ClassTimerVarsEventArgs)

End Class

Public Class ClassTimerVarsEventArgs
	Inherits EventArgs

    Public Property Feedrate As Integer
    Public Property Spindle As Integer
    Public Property Rapid As Integer

End Class

Использование:
Sub Main
	'create new instance of ClassTimerVars with initial values
	Dim ctv As ClassTimerVars = New ClassTimerVars(1,5,10)
	AddHandler ctv.DataHasBeenAdded, AddressOf ctv_DataHasBeenAdded
	
	'dispplay initial value
	Console.WriteLine(ctv.ToString())
	'add data #1
	ctv.AddData(2,8,11)	
	'add data #2
	ctv.AddData(Nothing,3,21)
	'add data #3
	ctv.AddData(12,4,Nothing)
	
End Sub


Выход:
ClassTimerVars totals - Feedrate: 1, Spindle: 5, Rapid: 10.
New data has been added! Feedrate: 2, Spindle: 8, Rapid: 11.
ClassTimerVars totals - Feedrate: 3, Spindle: 13, Rapid: 21.
New data has been added! Feedrate: 0, Spindle: 3, Rapid: 21.
ClassTimerVars totals - Feedrate: 3, Spindle: 16, Rapid: 42.
New data has been added! Feedrate: 12, Spindle: 4, Rapid: 0.
ClassTimerVars totals - Feedrate: 15, Spindle: 20, Rapid: 42.


Попробуй!


theskiguy

Хорошо, пожалуйста, потерпите меня, я никогда не делал этого раньше, поэтому мне нужно немного руководства. Я скопировал пример, который вы привели по ссылке, и он работает, но мне трудно понять, как этот пример может быть применен к моей ситуации. В этом примере есть 2 класса: класс counter и класс event args. Я предполагаю, что мне нужно будет изменить sub new в классе counter, чтобы принять 3 vars, которые я отслеживаю? Не здесь ли я должен также отслеживать, изменяются ли переменные, чтобы работать аналогично моему примеру выше? Тогда какова же цель класса event args?

Maciej Los

Проверьте обновленные ответ ;)

Graeme_Grant

Щедрый ответ. +5 :)

Maciej Los

Спасибо, Грэм.

Graeme_Grant

Установите точку останова и пройдите через его код. Вы увидите, как это работает.

theskiguy

Большое спасибо за такой хороший пример. Это действительно помогло мне понять, как все работает. У меня есть один вопрос. В приведенном вами примере он отслеживает итоги каждый раз, когда вызывается подпрограмма AddData. В моем примере из реальной жизни меня действительно не интересуют итоги, а просто изменились ли какие-либо значения. Имея это в виду, я внес некоторые незначительные изменения в подпрограмму AddData, чтобы сравнить вновь добавленные значения с существующими. Если все 3 переменные одинаковы, я выхожу из подпрограммы без вызова события, потому что мне не нужно обновлять базу данных. Если одна или несколько переменных изменились, я затем обновляю переменные класса до новых результатов, а затем продолжаю и вызываю событие. Это лучший способ справиться с этим или вы порекомендуете другой метод? Может быть, добавить логическое значение DataChanged к классу и аргументам событий?

Maciej Los

Зачем добавлять еще одну переменную? Если вы можете сравнить входящие данные с существующими, вы можете вызвать процедуру обновления базы данных только тогда, когда данные были изменены.
Овации
Мацей

Рейтинг:
2

Graeme_Grant

Опрос переменных с таймером будет пропускать события. Для этого требуется классика шаблон Observer[^].


Maciej Los

Хорошая идея!

Рейтинг:
0

Ralf Meier

Я бы сделал что-то вроде описанного в комментариях :
вы создаете одно событие для своего класса, которое запускается каждый раз, когда изменяется свойство.

Если вы хотите создать свой собственный список (ClassTimerVars), вы переопределяете Add-метод и перенаправляете сюда событие из вашего класса на собственное событие из вашего списка (AddHandler).
Если вы удаляете элемент из этого списка (или удаляете его), вы должны удалить обработчик из класса-элемента в метод списка.