2

I have a generic method which serialises some numeric config entries. Additionally, only for floating point types it must store culture info along. How can I tell that template param is of floating point type?

Here's what I tried:

    public void Bar<T>() where T : System.Numerics.INumber<T> {
        T aa = default(T);
        
        // this is ugly
        if (aa is float || aa is double)
        {
        }
        
        // error: The type 'T' cannot be used as type parameter 'TSelf' in the generic type
        // or method 'IFloatingPoint<TSelf>'. There is no boxing conversion or type parameter
        // conversion from 'T' to 'System.Numerics.IFloatingPoint<T>'.
        if (aa is IFloatingPoint<T>)
        {
        }
        
        // works fine and IFloatingPoint<T> would also work if T was constrained to
        // IFloatingPoint<T> (but I need to accept also integer types)
        if (aa is INumber<T>)
        {
        }
        

    }

Can I somehow rephrase aa is IFloatingPoint<T> expression (or use something similar) to make it work and gracefully check T against being of floating-point-like? Similarly to C++'s

if constexpr (std::is_floating_point_v<T>) {
}
Marcin Tarsier
  • 192
  • 2
  • 9
  • why the first condition is ugly? however, you don't need to instantiate a variable to compare the type: there's `typeof`. – Mario Vernari Jun 23 '23 at 09:58
  • 1
    `typeof(T) == typeof(float)`. The JIT recognises this pattern and will remove the checks, as well as code which is not reachable for the current `T` – canton7 Jun 23 '23 at 09:59
  • @canton7 a small nitpicky remark - if `T` is a value type (which is true for number types I know about though) =) – Guru Stron Jun 23 '23 at 11:43
  • 1
    @GuruStron I was waiting for someone to mention that ;) Thankfully all of the types which implement `INumber` are structs – canton7 Jun 23 '23 at 11:44
  • 1
    @canton7 was glad not to disappoint! – Guru Stron Jun 23 '23 at 11:45

2 Answers2

3

If you really want to check for IFloatingPoint<T> conformance, you can check:

typeof(T).GetInterface("IFloatingPoint`1") != null

But there really is nothing wrong with checking double and float individually if those are the only two types you are working with. You don't need a default value. Just do

typeof(T) == typeof(double) || typeof(T) == typeof(float)
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 1
    I'm not so keen on the first version, since that can be broken if someone adds their own version of `IFloatingPoint`. Would rather be more strict, like [this answer](https://stackoverflow.com/a/503359/1663001). Though all that goes away with the second version here anyway. – DavidG Jun 23 '23 at 10:05
  • @DavidG I thought about that as well, but someone else’s (a sane person’s)`IFloatingPoint` should also indicates that the type is a floating point number. It seems like OP doesn’t depend on any of the members that `IFloatingPoint` offers, it should be fine even if it’s not the built in type. OP just wants to add an extra culture info after all. – Sweeper Jun 23 '23 at 10:16
  • Yeah, that's fair, of course there may be future viewers of this post who are doing something else with the value. Either way, you did get my upvote. – DavidG Jun 23 '23 at 10:36
  • Well, C# is a strongly typed language, so I prefer to use exact types than just relying on "similar names", that's veeeery risky :) thanks @DavidG for this linked answer, that actually answers my question (at quite a runtime overhead, but oh well, it's C#). – Marcin Tarsier Jun 26 '23 at 07:42
2

As an alternative to the answer provided by Sweeper, and since you mention the is_floating_point function from C++, you can replicate that functionality like this:

public bool IsFloatingPoint<T>()
{
    return typeof(T)
        .GetInterfaces()
        .Any(i => i.IsGenericType &&
                  i.GetGenericTypeDefinition() == typeof(IFloatingPoint<>));
}

And now in your Bar method:

public void Bar<T>() 
    where T : System.Numerics.INumber<T>
{
    if (IsFloatingPoint<T>())
    {
        // Do the floating point stuff
    }
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • Note that this is very expensive compared to `if (typeof(T) == typeof(float) || typeof(T) == typeof(double))` etc – canton7 Jun 23 '23 at 10:48
  • 2
    @canton7 Indeed, but it does cover the case where another class can be added later that also implements `IFloatingPoint` – DavidG Jun 23 '23 at 11:18
  • @DavidG yes, indeed, I always try to make code future proof if anyone used any type implementing `IFloatingPoint` rather than explicit list of `typeof()`s. It's a bit sad we need to use costly LINQ for this, but apparently it resembles the most this simple and zero-cost compile-time solution from C++. Therefore, I mark it as an accepted solution. – Marcin Tarsier Jun 26 '23 at 07:39