参考列表中的属性

时间:2017-08-03 11:55:25

标签: c#

我正在制作一个简单的报警处理程序,我希望有一组固定的报警来不断检查是否应该设置或清除。我有一个包含警报对象的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的构造函数的逻辑存在问题。这就是我发布这个问题的原因,因为我不确定如何实现这个行为。但我只是举例说明了我真正喜欢实现的行为。 : - )

2 个答案:

答案 0 :(得分:3)

更新2 - 根据您的要求

我已多次重读您的问题,而且我仍然不太确定您案件的具体情况。我认为您正在使用定义的类,这些类的名称对您来说是有意义的,但对于在您的应用程序中缺乏经验的读者而言则不然。

例如,我开始推断您的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是引用类型,它们在功能上是等效的,但如果它是值类型,则仅适用第二个答案。

注意:我省略了其他构造函数变量,因为它们与示例无关。
我也将假设(为了示例)您的检查将始终评估为布尔值(基础值是布尔值,还是比较字符串值等)

1。更好的答案

仅当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对象,那么上面的内容将无法工作,因为它不断引用相同的对象。

2。较小的解决方案

但是,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);

从功能上讲,它会做同样的事情。但是您的方法可能存在一些实施障碍:

  • 如果您阅读的属性可以有不同的类型(string,int,bool),那么在尝试定义Func<T,U>变量时会遇到问题(因为U的类型可能会发生变化)
  • 您的版本要求您为每种类型的错误检查创建唯一的构造函数。您依赖于唯一的构造函数签名来了解要实现的错误检查类型。

转过身来,我的版本应该有一些好处:

  • 属性的类型无关紧要。你没有写一个Func<T,U> 检索一个属性的值(它可以有不同的类型),而是你写了一个Func<T,bool> 检查检索到的正确值(因此,输出始终为bool
  • 如果您有各种各样的错误检查(例如value < maxvalue > minmin < 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(); 
}