Добавление реализации интерфейса в ExpandoObject
Я передаю информацию между базой данных SQL и ПЛК, используя сторонние библиотеки OPC.
По сути, есть две транзакции.
Информация, передаваемая из ПЛК на сервер SQL, имеет статическую типизацию. Очень специфические данные собираются ПЛК и передаются в базу данных SQL.
Информация, передаваемая с сервера SQL в ПЛК, динамически типизирована и может быть ограничена одним свойством или сотнями.
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 и другие методы в базовый класс, но не могу этого сделать, поскольку базовый класс не знает о свойствах Response и Request, найденных в OpcClient. Могу ли я как-то перенести интерфейс ITransaction в базовый класс и при этом приспособить динамическую реализацию?
1 ответ
Вы можете подкласс DynamicObject
(который действует так же, как ExpandoObject) и сделать свою собственную версию, которая реализует ITransaction
, Это позволяет вам перемещать ITransaction
ограничение до базового класса.
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;
}
}