The following extension method returns a plural version of s only when value is not equal to 1.
public static string Pluralize<T>(this string s, T value) where T : struct, IComparable
{
return (value.CompareTo(1) == 0) ? s : s.Pluralize();
}
This method works fine when value is type int. But if it's a long, a double, or pretty much any other type, it throws an exception.
System.ArgumentException: 'Object must be of type Double.'
Does anyone know of a generic way to compare value to 1 even if T is not int?
Otherwise, I will just create an overload for each supported type.
CodePudding user response:
When using generics you give up most ability to work with the values. Generic constraints give some of that back, but unfortunately there is still no way to constrain types to the integral numeric types, even if that has been talked about for many c# versions now.
If you can guarantee that the method will only be called with types convertible to integers you could use Convert.ToInt32(value) to convert it, allowing the result to be compared. But this will fail at runtime for non-numeric types.
You could also use typechecks for each type you want to handle:
switch (value)
{
case float fValue:
return fValue == 1f ? s : s.Pluralize();
...
default:
return "fail";
}
This would be more code, but would let you handle the default case without resorting to catching exceptions.
CodePudding user response:
There are 2 solutions you can apply (3 if you want to risk using preview features).
using dynamic:
dynamic v = value;
return v == 1 ? s : s "--";
Since the number of possible primitives is exhaustive, you can also simply use a switch or pattern matching, like so:
return value switch
{
1 or 1L or 1UL or 1.0d or 1.0f or 1.0m => s, // covers int, long, ulong, double, float and decimal
uint x when x == 1 => s,
short x when x == 1 => s,
ushort x when x == 1 => s,
byte x when x == 1 => s,
sbyte x when x == 1 => s,
char x when x == 1 => s,
_ => s.Pluralize()
};
(it's unfortunate that there are no literal representations for uint, (u)short, (s)byte and char...)
However, there's also the new 'Generic Math' feature on the C#/.Net roadmap that you could leverage for this. Unfortunately it didn't make the cut for .NET 6, but you can already try it (in preview). This would allow you to implement the generic function as follows:
static string Pluralize<T>(this string s, INumber<T> value) where T : INumber<T>
=> value.Equals(T.One) ? s : s.Pluralize();
To make this work, you need to add this to your csproj file:
<PropertyGroup>
<LangVersion>preview</LangVersion>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Experimental" Version="6.0.2-mauipre.1.22054.8" />
</ItemGroup>
