-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Background and motivation
The IParsable<T> interface was added in .NET 7 to support using generic types that can be parsed from a string (e.g., int and double). However, this was not extended to include string, which presents some consistency issues.
My specific use-case for this is a generic class for reading and writing query string parameters in Blazor WASM (see below). I'd like to have a single generic class which can be used as e.g. QueryParam<int> or QueryParam<string>, but the fact the string doesn't implement IParsable makes this impossible. Instead, I have to make a separate, non-generic class for handling strings, even though they should be the "easiest" case. I imagine that this issue would extend to many other use-cases for reading values from strings.
The class I'd like to be able to use (simplified):
public sealed class QueryParam<T> where T : ISpanParsable<T>, IFormattable
{
private T _value;
private readonly NavigationManager _navManager;
private readonly string _name;
public QueryParam(NavigationManager navManager, string name, T defaultValue)
{
_navManager = navManager;
_name = name;
var uri = navManager.ToAbsoluteUri(navManager.Uri);
_value = QueryHelpers.ParseQuery(uri.Query).TryGetValue(name, out var val)
&& T.TryParse(val.ToString(), CultureInfo.InvariantCulture, out var parsed)
? parsed
: defaultValue;
}
public T Value
{
get => _value;
set
{
_value = value;
var stringValue = value.ToString(null, CultureInfo.InvariantCulture);
_navManager.NavigateTo(_navManager.GetUriWithQueryParameter(_name, stringValue), replace: true);
}
}
}Then using it:
var name = new QueryParam<string>(_navManager, "name", "");
var age = new QueryParam<int>(_navManager, "age", 0);Note: The above class would also require string to implement IFormattable in a similar fashion. However, much of the benefit could be had with just IParsable.
API Proposal
namespace System;
// These implementations use explicit interface implementation, so that the methods
// are not directly visible on string.
public class String : IParsable<String>
{
// returns s
static string IParsable<String>.Parse(string s);
// returns true if s is non-null, and sets result = s.
static bool IParsable<String>.TryParse(string? s, IFormatProvider? provider, out string result);
}API Usage
A simpler (and less useful) example than the one above:
public static IEnumerable<T> Separate<T>(string source, char separator) where T : IParsable<T>
{
return source.Split(separator)
.Select(x => T.Parse(x, CultureInfo.InvariantCulture));
}
var ints = Separate<int>("11,12,13", ',');
var strings = Separate<string>("aa,bb,cc", ',');Alternative Designs
It may be preferable to implement ISpanParsable<string> instead, for the same consistency reasons as above.
Risks
The obvious issue here is that these methods don't make much sense in isolation, so users may be confused by the presence of a string.Parse method.