向 ExpandoObject 添加接口实现

Adding Interface Implementation to ExpandoObject

我正在使用第 3 方 OPC 库在 SQL 数据库和 PLC 之间传递信息。

基本上有两个事务。

从 PLC 传递到 SQL 服务器的信息是静态类型的。非常具体的数据由 PLC 捕获并传递到 SQL 数据库。

从 SQL 服务器传递到 PLC 的信息是动态输入的,可能限于单个 属性 或数百个。

ITransaction.cs

public interface ITransaction : INotifyPropertyChanged
{
    short Response { get; set; }

    bool Request { get; set; }

    void Reset();
}

BaseTransaction.cs

internal abstract class BaseTransaction<T> : IDisposable
    where T : class, INotifyPropertyChanged
{
    private T _opcClient;

    protected T OpcClient
    {
        get { return _opcClient; }
        set
        {
            if (_opcClient != value)
            {
                OnOpcClientChanging();
                _opcClient = value;
                OnOpcClientChanged();
            }
        }
    }

    protected abstract void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e);

    private void OnOpcClientChanged()
    {
        if (_opcClient != null)
        {
            _opcClient.PropertyChanged += OnOpcClientPropertyChanged;

            OpcManager = new OpcManager(_opcClient);
        }
    }

    private void OnOpcClientChanging()
    {
        if (_opcClient != null)
            _opcClient.PropertyChanged -= OnOpcClientPropertyChanged;
    }
}

StaticTransaction.cs

internal abstract class StaticTransaction<T> : BaseTransaction<T>
    where T : class, ITransaction, new()
{
    public StaticTransaction()
    {
        OpcClient = new T();
    }

    protected override void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "Response":
                ProcessResponse(OpcClient.Response);
                break;
            case "Request":
                ProcessRequest(OpcClient.Request);
                break;
        }
    }
}

DynamicTransaction.cs

internal abstract class DynamicTransaction : BaseTransaction<ExpandoObject>
{
    protected new dynamic OpcClient
    {
        get { return base.OpcClient as dynamic; }
    }

    public DynamicTransaction()
    {
        dynamic opcClient = new ExpandoObject();

        opcClient.Request = false;
        opcClient.Response = 0;

        // Access database, use IDictionary interface to add properties to ExpandoObject.

        opcClient.Reset = new Action(Reset);

        base.OpcClient = opcClient;
    }

    protected override void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "Response":
                ProcessResponse(OpcClient.Response);
                break;
            case "Request":
                ProcessRequest(OpcClient.Request);
                break;
        }
    }

    private void Reset()
    {
        // Use IDictionary interface to reset dynamic properties to defaults.

        OpcClient.Request = false;
        OpcClient.Response = 0;
    }
}

如图所示,StaticTransaction 和 DynamicTransaction 具有相同的 OnOpcClientPropertyChanged 实现以及未显示的其他方法。我想将 OnOpcClientPropertyChanged 和其他方法引入基础 class 但我无法这样做,因为基础 class 不知道 OpcClient 中的 Response 和 Request 属性。我能否以某种方式将接口 ITransaction 引入基础 class 并仍然适应动态实现?

您可以子class DynamicObject(就像 ExpandoObject 一样)并制作您自己的实现 ITransaction 的版本。这使您可以将 ITransaction 约束向上移动到基础 class.

BaseTransaction.cs

internal abstract class BaseTransaction<T> : IDisposable where T : class, ITransaction
{
    private T _opcClient;

    protected T OpcClient
    {
        get { return _opcClient; }
        set
        {
            if (_opcClient != value)
            {
                OnOpcClientChanging();
                _opcClient = value;
                OnOpcClientChanged();
            }
        }
    }


    private void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "Response":
                ProcessResponse(OpcClient.Response);
                break;
            case "Request":
                ProcessRequest(OpcClient.Request);
                break;
        }
    }

    protected abstract void ProcessResponse(short opcClientResponse);

    protected abstract void ProcessRequest(bool opcClientRequest);

    private void OnOpcClientChanged()
    {
        if (_opcClient != null)
        {
            _opcClient.PropertyChanged += OnOpcClientPropertyChanged;

            OpcManager = new OpcManager(_opcClient);
        }
    }

    private void OnOpcClientChanging()
    {
        if (_opcClient != null)
            _opcClient.PropertyChanged -= OnOpcClientPropertyChanged;
    }
}

StaticTransaction.cs

internal abstract class StaticTransaction<T> : BaseTransaction<T>
where T : class, ITransaction, new()
{
    public StaticTransaction()
    {
        OpcClient = new T();
    }
}

DynamicTransactionObject.cs

internal class DynamicTransactionObject : DynamicObject, ITransaction, IDictionary<string, object>
{

    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();

    public DynamicTransactionObject()
    {
        //Set initial default values for the two properties to populate the entries in the dictionary.
        _data[nameof(Response)] = default(short);
        _data[nameof(Request)] = default(bool);
    }

    public short Response
    {
        get
        {
            return (short)_data[nameof(Response)];
        }
        set
        {
            if (Response.Equals(value))
                return;

            _data[nameof(Response)] = value;
            OnPropertyChanged();
        }
    }

    public bool Request
    {
        get
        {
            return (bool)_data[nameof(Request)];
        }
        set
        {
            if (Request.Equals(value))
                return;

            _data[nameof(Request)] = value;
            OnPropertyChanged();
        }
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _data.Keys;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {

        return _data.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        object oldValue;
        _data.TryGetValue(binder.Name, out oldValue)
        _data[binder.Name] = value;
        if(!Object.Equals(oldValue, value)
           OnPropertyChanged(binder.Name);
        return true;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #region IDictionary<string,object> members
    IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)_data).GetEnumerator();
    }

    void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
    {
        ((ICollection<KeyValuePair<string, object>>)_data).Add(item);
    }

    void ICollection<KeyValuePair<string, object>>.Clear()
    {
        _data.Clear();
    }

    bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
    {
        return ((ICollection<KeyValuePair<string, object>>)_data).Contains(item);
    }

    void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {
        ((ICollection<KeyValuePair<string, object>>)_data).CopyTo(array, arrayIndex);
    }

    bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
    {
        return ((ICollection<KeyValuePair<string, object>>)_data).Remove(item);
    }

    int ICollection<KeyValuePair<string, object>>.Count
    {
        get { return _data.Count; }
    }

    bool ICollection<KeyValuePair<string, object>>.IsReadOnly
    {
        get { return ((ICollection<KeyValuePair<string, object>>)_data).IsReadOnly; }
    }

    bool IDictionary<string, object>.ContainsKey(string key)
    {
        return _data.ContainsKey(key);
    }

    void IDictionary<string, object>.Add(string key, object value)
    {
        _data.Add(key, value);
    }

    bool IDictionary<string, object>.Remove(string key)
    {
        return _data.Remove(key);
    }

    bool IDictionary<string, object>.TryGetValue(string key, out object value)
    {
        return _data.TryGetValue(key, out value);
    }

    object IDictionary<string, object>.this[string key]
    {
        get { return _data[key]; }
        set { _data[key] = value; }
    }

    ICollection<string> IDictionary<string, object>.Keys
    {
        get { return _data.Keys; }
    }

    ICollection<object> IDictionary<string, object>.Values
    {
        get { return _data.Values; }
    }
#endregion
}

DynamicTransaction.cs

internal abstract class DynamicTransaction : BaseTransaction<DynamicTransactionObject>
{
    protected new dynamic OpcClient
    {
        get { return base.OpcClient as dynamic; }
    }

    public DynamicTransaction()
    {
        var opcClient = new DynamicTransactionObject();

        // Access database, use IDictionary<string,object> interface to add properties to DynamicObject.

        base.OpcClient = opcClient;
    }
}