向 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;
}
}
我正在使用第 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;
}
}