我正在制作一个简单的报警处理程序,我希望有一组固定的报警来不断检查是否应该设置或清除。我有一个包含警报对象的ObservableCollection属性。
我非常希望让Alarm对象包含对属性的某种形式的引用,该属性包含应该对其进行评估的值。这样我就可以通过ObservableCollection枚举并根据评估结果设置AlarmStatus成员。但是我怎么能这样做呢?
从我所看到的,C#中不允许使用属性byref。我想使用属性的名称和路径作为字符串,然后搜索它可以完成,但这似乎有点“超过顶部”。
以下是AlarmHandler类的近似值:
public class AlarmHandler {
public ObservableCollection<Alarm> AlarmList { get; set; }
public CancellationTokenSource AlarmEvaluatorCts { get; set; }
public Controller Controller { get; set; }
public AlarmHandler(Controller controller) {
Controller = controller;
AlarmEvaluatorCts = new CancellationTokenSource();
AlarmList = new ObservableCollection<Alarm>();
AlarmList.Add(new Alarm(AlarmId.x0010_TELEMETRY_STATUS, AlarmType.MINOR, ComparisonType.NTEQ, true, null, ref Controller.SerialPortHandler.IsSerialPortOpen));
AlarmList.Add(new Alarm(AlarmId.x0011_INPUT_VOLTAGE, AlarmType.MAJOR, ComparisonType.GTLT, 100, 200, ref Controller.RovModel.InputVoltage));
AlarmList.Add(new Alarm(AlarmId.x0012_TEMPERATURE, AlarmType.MAJOR, ComparisonType.LT, 80, null, ref Controller.RovModel.Motor.Temperature));
Task.Factory.StartNew(() => AlarmEvaluator(), TaskCreationOptions.LongRunning);
}
private void AlarmEvaluator() {
while (!AlarmEvaluatorCts.IsCancellationRequested) {
foreach (Alarm alarm in AlarmList) {
// Check each Alarm object if the input value passes or fails given comparison parameters,
// and set or clear alarm status flag accordingly.
}
}
}
}
当应用程序启动时,它首先实例化各种处理程序类,并在最后实例化AlarmHandler类以监视其他处理程序类中的值。
在构造函数中,我首先存储对Controller对象的引用(管理所有处理程序和东西的对象),然后为AlarmEvaluator方法设置CancellationTokenSource,实例化ObservableCollection属性并向其添加警报(用于监控串口状态的报警,输入电压在100-200伏范围内的报警监控,以及监控电机温度低于80℃的报警。最后我在一个单独的线程中启动AlarmEvaluator方法。只要应用程序正在运行就运行。
以下是Alarm类的近似值:
public class Alarm : BindableBase {
private AlarmStatus _alarmStatus = AlarmStatus.CLEARED;
public AlarmStatus AlarmStatus {
get { return _alarmStatus; }
set {
SetNotify(ref _alarmStatus, value);
Timestamp = DateTime.Now;
}
}
public AlarmId AlarmId { get; private set; }
public AlarmType AlarmType { get; private set; }
public ComparisonType ComparisonType { get; private set; }
public double LowThreshold { get; private set; }
public double HighThreshold { get; private set; }
public double InputValue { get; private set; }
public DateTime Timestamp { get; private set; }
public Alarm(AlarmId alarmId, AlarmType alarmType, ComparisonType comparisonType, double lowThreshold, double highThreshold, ref double inputValue) {
AlarmId = alarmId;
AlarmType = alarmType;
ComparisonType = comparisonType;
LowThreshold = lowThreshold;
HighThreshold = highThreshold;
InputValue = inputValue;
}
}
注意:我知道第一个警报是bool类型和Alarm类没有定义允许bool类型inputValue的构造函数的逻辑存在问题。这就是我发布这个问题的原因,因为我不确定如何实现这个行为。但我只是举例说明了我真正喜欢实现的行为。 : - )
答案 0 :(得分:3)
我已多次重读您的问题,而且我仍然不太确定您案件的具体情况。我认为您正在使用定义的类,这些类的名称对您来说是有意义的,但对于在您的应用程序中缺乏经验的读者而言则不然。
例如,我开始推断您的Alarm
类实际上是一个对象,其主要目标更好地描述为ErrorCheck
,这可能会也可能不会导致错误检查失败时发出警报。这是正确的解释吗?
无论如何,我确实认为解决方案的关键在于你添加的这行代码:
AlarmList.Add(new Alarm(AlarmId.x0010_TELEMETRY_STATUS, AlarmType.MINOR, ComparisonType.NTEQ, true, null, ref Controller.SerialPortHandler.IsSerialPortOpen));
警报构造函数:
new Alarm(AlarmId.x0010_TELEMETRY_STATUS, AlarmType.MINOR, ComparisonType.NTEQ, true, null, ref Controller.SerialPortHandler.IsSerialPortOpen)
推断您希望此Alarm
对象重复检查x0010_TELEMETRY_STATUS
的值是否正确?这是您想要读取“byref”的值吗?
这里有两个可能的答案。如果您的AlarmId
是引用类型,它们在功能上是等效的,但如果它是值类型,则仅适用第二个答案。
注意:我省略了其他构造函数变量,因为它们与示例无关。
我也将假设(为了示例)您的检查将始终评估为布尔值(基础值是布尔值,还是比较字符串值等)
仅当AlarmId
是引用类型时才有效!
在原始示例中,您传递了x0010_TELEMETRY_STATUS
属性的值。这是错误的,因为值会随着时间的推移而变化,而Alarm
需要读取这些更新的值。
new Alarm(AlarmId.x0010_TELEMETRY_STATUS);
相反,您想要的是提供定义,了解将来如何阅读该值(alarmId => alarmId.x0010_TELEMETRY_STATUS
),以及对象(引用类型!),您将在其上执行此定义(AlarmId
):
new Alarm(alarmId => alarmId.x0010_TELEMETRY_STATUS, AlarmId);
您如何将其存储在Alarm
课程中?
public class Alarm
{
private Func<AlarmId, bool> _theErrorCheck { get; set; }
private AlarmId _theObject { get; set; }
public Alarm(Func<AlarmId, bool> errorCheck, AlarmId obj)
{
this._theErrorCheck = errorCheck;
this._theObject = obj;
}
private bool PerformCheck()
{
return _theErrorCheck.Invoke(_theObject);
}
}
每当您拨打PerformCheck()
时,您都会读取当前值(即在调用方法时)。
但是,请注意,仅当AlarmId
是引用类型时,此方法才有效。如果它不是引用类型,或者只要收到更新就创建新的AlarmId
对象,那么上面的内容将无法工作,因为它不断引用相同的对象。
但是,AlarmId
是值类型的最佳解决方案,或者您始终实例化新的AlarmId
个对象!
这里唯一的区别是,不是在构造函数中传递AlarmId
一次;每次调用时,您都会通过AlarmId
方法传递PerformCheck()
。
new Alarm(alarmId => alarmId.x0010_TELEMETRY_STATUS);
然后:
public class Alarm
{
private Func<AlarmId, bool> _theErrorCheck { get; set; }
public Alarm(Func<AlarmId, bool> errorCheck)
{
this._theErrorCheck = errorCheck;
}
private bool PerformCheck(AlarmId theObject)
{
return _theErrorCheck.Invoke(theObject);
}
}
但正如你所看到的,它几乎是一回事。
我也看到你的Alarm
班级有门槛。我从中推断出你已经分离了值的检索,以及检索到的值的 validation 。
然而,我实际上是再次把这些放在一起的支持者。
假设您需要一个警报来检查给定值是否介于1和5之间。您的(简化)构造函数将类似于:
new Alarm(alarmId => alarmId.GivenValue, 1, 5);
但是,我更愿意将其浓缩为一个声明:
new Alarm(alarmId => alarmId.GivenValue >= 1 && alarmId.GivenValue <= 5);
从功能上讲,它会做同样的事情。但是您的方法可能存在一些实施障碍:
Func<T,U>
变量时会遇到问题(因为U
的类型可能会发生变化) 转过身来,我的版本应该有一些好处:
Func<T,U>
检索一个属性的值(它可以有不同的类型),而是你写了一个Func<T,bool>
检查检索到的正确值(因此,输出始终为bool
)value < max
,value > min
,min < value < max
,值是偶数,...),那么将其浓缩为一个是有益的单个lambda方法。这意味着您的Alarm
不会根据错误检查方法更改结构。答案 1 :(得分:1)
要获取对属性的引用,只需将具有该属性的对象实例存储在Alarm对象中,并在需要更新AlarmStatus时调用它:
public AlarmStatusProvider
{
...
string Data { get { return this.data; } }
}
public class Alarm
{
private readonly AlarmStatusProvider provider;
public Alarm(AlarmStatusProvider provider)
{
this.provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public void UpdateAlarmStatus()
{
// update AlarmStatus based on this.provider.Data
}
public AlarmId AlarmId { get; private set; }
public AlarmType AlarmType { get; private set; }
public Comparison Comparison { get; private set; }
public double LowThreshold { get; private set; }
public double HighThreshold { get; private set; }
public DateTime Timestamp { get; private set; }
public AlarmStatus AlarmStatus { get; private set; }
}
然后您只需枚举它就可以更新observableCollection中的警报:
foreach(var alarm in observableCollectionOfAlarms) { alarm.Update(); }
但我认为我宁愿公开AlarmStatus
属性并在外部更新它:
foreach(var alarm in observableCollectionOfAlarms)
{
alarm.AlarmStatus = RecalculateStatus();
}