У меня возникла мысль: я мог бы заменить поле DateTime
в классе параметров типом оболочки, а затем реализовать анализ. Мы меняем тип поля следующим образом:
private class TestOptions
{
//public DateTime TestValue { get; set; }
public RoundtripDateTime TestValue { get; set; }
}
Мы создаем тип оболочки (я сделал его struct
, но class
также работает), включая неявные преобразования в и из DateTime
(так что по крайней мере некоторый код не нужно менять) и хорошие Parse()
и ToString()
методы. Мы включили атрибут [TypeConverter]
, чтобы связыватель конфигурации обнаружил класс преобразователя.
[TypeConverter(typeof(RoundtripDateTimeTypeConverter))]
public struct RoundtripDateTime
{
public RoundtripDateTime(DateTime value)
{
Value = value;
}
public DateTime Value { get; set; }
public static implicit operator DateTime(RoundtripDateTime roundTripDateTime) => roundTripDateTime.Value;
public static implicit operator RoundtripDateTime(DateTime dateTime) => new RoundtripDateTime(dateTime);
public override string ToString() => Value.ToString("o");
public static RoundtripDateTime Parse(string s) => DateTime.Parse(s, null, DateTimeStyles.RoundtripKind);
public static RoundtripDateTime Parse(string s, CultureInfo culture) => DateTime.Parse(s, culture, DateTimeStyles.RoundtripKind);
}
Наконец, мы включили новую реализацию TypeConverter
, которая будет обрабатывать экземпляры оболочки. (Я обманул и посмотрел на источник на DateTimeConverter
, чтобы узнать, что он уже делал, но на самом деле это гораздо проще , потому что он всегда работает в незнании культуры. [Редактировать: Для преобразования из string
, добавил культуру, чтобы помочь Parse()
иметь дело с датами, которые не в формате ISO8601. Это все еще довольно просто.])
public class RoundtripDateTimeTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string stringValue)
{
var text = stringValue.Trim();
if (text == string.Empty)
{
return (RoundtripDateTime)DateTime.MinValue;
}
else
{
try
{
return RoundtripDateTime.Parse(text, culture ?? CultureInfo.InvariantCulture);
}
catch (FormatException e)
{
throw new FormatException($"'{value}' is not a valid value for {nameof(RoundtripDateTime)}.", e);
}
}
}
else
{
return base.ConvertFrom(context, culture, value);
}
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value is RoundtripDateTime roundtripDateTimeValue)
{
return (roundtripDateTimeValue.Value == DateTime.MinValue)
? string.Empty
: roundtripDateTimeValue.ToString();
}
else
{
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
Все это может потребовать некоторой настройки, но я думаю, что у меня есть что-то, с чем я могу работать.
Если у вас есть идеи получше, не стесняйтесь добавить еще один ответ.