具有以下课程:
public class DeviceParameter
{
public string Key { get; set; }
public Guid DeviceId { get; set; }
public string Value { get; set; }
}
设备可以具有很多不同类型的参数,但它们都以字符串形式存储在数据库中。
public abstract class DeviceValueTypedParameter<TValue>
{
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public DeviceValueTypedParameter(string codeName)
{
this.CodeName = codeName;
}
}
DeviceValueTypedParameter 是一个抽象,具有类型化的值( TValue )在参数的C#上使用值,而不是使用我们从数据库中获得的字符串。 DeviceValueTypedDeviceParameter和DeviceParameter之间没有继承关系,因为我想按组成将TValue转换为字符串。
public class ArmingStatusParameter : DeviceValueTypedParameter<ArmingStatuses>
{
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME)
{
}
}
public enum ArmingStatuses
{
Unknown,
Armed,
Disarmed,
}
ArmingStatusParameter 是可以存在的类型化Parameter的示例,其中值是ArmingStatuses的Enum。可以存在的其他类型为DateTimes,int32,double等。
我已经完成了从Typed值到String的转换,但是现在我在努力如何正确地进行从string到Typed值的转换。
尝试了不同的方法:
选项1:易于实现,但违反了
的POCO
ArmingStatusParameter。人们可能会忘记实施隐式/显式运算符,并且错误只会在编译时发生。
选项2:违反了接口隔离原则(ISP),因为直接访问转换是必需的。
选项3:它可以工作,但是人们将不得不创建很多类,并且代码太冗长。对于每个不同的参数,都需要实例化一个新的{X} TypedParameterConverter。
选项4:似乎是最好的选择,但是我在“使其工作”方面遇到麻烦
我在想这样的事情:
public interface IDeviceValueTypedParameterConverter
{
bool TryConvert<T, TValue>(DeviceParameter deviceParameter,
DeviceValueTypedParameter<TValue> deviceValueTypedParameter)
where T : DeviceValueTypedParameter<TValue>;
}
public class DeviceValueTypedParameterConverter : IDeviceValueTypedParameterConverter
{
public bool TryConvert<T, TValue>(DeviceParameter inputParameter,
DeviceValueTypedParameter<TValue> outputParameter)
where T : DeviceValueTypedParameter<TValue>
{
bool result = true;
if (inputParameter == null)
{
throw new NullReferenceException($"DeviceValueTypedParameter:'{typeof(T)}' must be initialized first");
}
if (inputParameter.Value is int)
{
result = int.TryParse(inputParameter.Value, out int temp);
outputParameter.Value = (TValue)temp;
}
else if (inputParameter.Value is Enum)
{
// some other code to convert the Enum's
}
// more else ifs one for each type
// (...)
else
{
result = false;
}
outputParameter.DeviceId = inputParameter.DeviceId;
return result;
}
}
问题:
答案 0 :(得分:0)
这是我尝试进行的工作-我不确定它是否违反了您未解释(或确实解释)的某些细节。由于cast v
参数不能使用多态性,因此我创建了一个接口来表示类型化参数基类中的常用功能。由于没有静态虚拟方法,因此我使用对象方法并创建了一个结果对象,该对象将在可能的转换时使用。
我没有看到转换方法具有多个实例或需要接口的理由,因此我将其创建为单个静态方法。我使用了out
来捕获从传入类型访问的参数所需的转换类型,并且不得不通过enum
进行棘手的转换以处理对object
参数值的赋值字段,因为C#没有分配的类型切换功能。请注意,如果out
方法不能正确过滤所有情况并且IsPossible
失败,则可能会导致运行时错误。
ChangeType
现在您可以像这样使用它:
public enum ValueParseTypes {
Enum,
DateTime,
Int
}
public interface IDeviceValueTypedDeviceParameter<TValue> {
string CodeName { get; }
TValue Value { get; set; }
Guid DeviceId { get; set; }
ValueParseTypes ParseType { get; set; }
bool IsPossibleValue(DeviceParameter aValue);
}
public abstract class DeviceValueTypedDeviceParameter<TValue> : IDeviceValueTypedDeviceParameter<TValue> {
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public ValueParseTypes ParseType { get; set; }
public DeviceValueTypedDeviceParameter(string codeName, ValueParseTypes parseType) {
this.CodeName = codeName;
this.ParseType = parseType;
}
public virtual bool IsPossibleValue(DeviceParameter aValue) => false;
}
public class ArmingStatusParameter : DeviceValueTypedDeviceParameter<ArmingStatuses> {
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME, ValueParseTypes.Enum) {
}
static HashSet<string> ArmingStatusesNames = Enum.GetNames(typeof(ArmingStatuses)).ToHashSet();
public override bool IsPossibleValue(DeviceParameter aValue) => ArmingStatusesNames.Contains(aValue.Value);
}
public enum ArmingStatuses {
Unknown,
Armed,
Disarmed,
}
public class PoweredOnStatusParameter : DeviceValueTypedDeviceParameter<DateTime> {
public const string CODE_NAME = "PoweredOn";
public PoweredOnStatusParameter() : base(CODE_NAME, ValueParseTypes.DateTime) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => DateTime.TryParse(aValue.Value, out _);
}
public class VoltageStatusParameter : DeviceValueTypedDeviceParameter<int> {
public const string CODE_NAME = "PoweredOn";
public VoltageStatusParameter() : base(CODE_NAME, ValueParseTypes.Int) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => Int32.TryParse(aValue.Value, out _);
}
public static class DeviceValueTypedParameterConverter {
public static bool TryConvert<TValue>(DeviceParameter inputParameter, IDeviceValueTypedDeviceParameter<TValue> outputParameter)
where TValue : struct {
if (inputParameter == null)
throw new ArgumentNullException(nameof(inputParameter));
else if (outputParameter == null)
throw new ArgumentNullException(nameof(outputParameter));
bool result = false;
if (outputParameter.IsPossibleValue(inputParameter)) {
outputParameter.DeviceId = inputParameter.DeviceId;
switch (outputParameter.ParseType) {
case ValueParseTypes.Enum:
if (Enum.TryParse(inputParameter.Value, out TValue typedValue)) {
outputParameter.Value = typedValue;
result = true;
}
break;
case ValueParseTypes.DateTime:
if (DateTime.TryParse(inputParameter.Value, out var dtValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(dtValue, typeof(TValue));
result = true;
}
break;
case ValueParseTypes.Int:
if (Int32.TryParse(inputParameter.Value, out var intValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(intValue, typeof(TValue));
result = true;
}
break;
}
}
return result;
}
}