I want to make safe, generic extension method for string?
parsing:
/// <summary>
/// Returns parsed value if success, otherwise default value
/// </summary>
public static T? ParseTo<T>(this string? value, IFormatProvider? formatProvider = null) where T : IParsable<T>
{
return T.TryParse(value, formatProvider, out var result) ? result : default;
}
This works like that:
"2022-12-20".ParseTo<DateTime>(); // (DateTime)2022-12-20
"".ParseTo<DateTime>(); // (DateTime)0001-01-01
How can I make it accept nullable types? I.e.:
"2022-12-20".ParseTo<DateTime?>(); // (DateTime?)2022-12-20
"".ParseTo<DateTime?>(); // (DateTime?)null
My intermediary solution is to have additional method:
/// <summary>
/// returns null if value is default or value otherwise
/// </summary>
public static T? NullIfDefault<T>(this T value) where T : struct
{
return EqualityComparer<T>.Default.Equals(value, default) ? null : value;
}
which works as expected:
[Test]
public async Task ParseCorrectly()
{
DateTime? date1 = string.Empty.ParseTo<DateTime>();
DateTime? date2 = string.Empty.ParseTo<DateTime>().NullIfDefault();
DateTime date3 = string.Empty.ParseTo<DateTime>();
DateTime date4 = "2022-12-20".ParseTo<DateTime>();
DateTime? date5 = "2022-12-20".ParseTo<DateTime>().NullIfDefault();
DateTime date6 = ((string?)null).ParseTo<DateTime>();
DateTime? date7 = ((string?)null).ParseTo<DateTime>().NullIfDefault();
Assert.Multiple(() =>
{
Assert.That(date1, Is.EqualTo((DateTime)default));
Assert.That(date2, Is.Null);
Assert.That(date3, Is.EqualTo((DateTime)default));
Assert.That(date4, Is.EqualTo(DateTime.Parse("2022-12-20")));
Assert.That(date5, Is.EqualTo(DateTime.Parse("2022-12-20")));
Assert.That(date6, Is.EqualTo((DateTime)default));
Assert.That(date7, Is.Null);
});
}
CodePudding user response:
AFAIK there is no nice solution to this problem due to the fact that generic constraints are not a part of method signature (there are some workarounds - for example see here, but in general it is not considered a good approach). I would suggest instead of adding NullIfDefault
method and chaining it - create a specialized method for value types:
public static T? ParseStructToNullable<T>(this string? value, IFormatProvider? formatProvider = null)
where T : struct, IParsable<T>
=> T.TryParse(value, formatProvider, out var result)
? result
: null;
"2022-12-20".ParseTo<DateTime>(); // (DateTime)2022-12-20
"".ParseTo<DateTime>(); // (DateTime)0001-01-01
var structToNullable = "2022-12-20".ParseStructToNullable<DateTime>(); // (DateTime)2022-12-20
var structTo = "".ParseStructToNullable<DateTime>(); // null