Is there a constraint that restricts my generic method to numeric types?

后端 未结 21 2603
栀梦
栀梦 2020-11-21 05:48

Can anyone tell me if there is a way with generics to limit a generic type argument T to only:

  • Int16
  • Int32
相关标签:
21条回答
  • 2020-11-21 06:28

    What is the point of the exercise?

    As people pointed out already, you could have a non-generic function taking the largest item, and compiler will automatically convert up smaller ints for you.

    static bool IntegerFunction(Int64 value) { }
    

    If your function is on performance-critical path (very unlikely, IMO), you could provide overloads for all needed functions.

    static bool IntegerFunction(Int64 value) { }
    ...
    static bool IntegerFunction(Int16 value) { }
    
    0 讨论(0)
  • 2020-11-21 06:29

    This limitation affected me when I tried to overload operators for generic types; since there was no "INumeric" constraint, and for a bevy of other reasons the good people on stackoverflow are happy to provide, operations cannot be defined on generic types.

    I wanted something like

    public struct Foo<T>
    {
        public T Value{ get; private set; }
    
        public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
        {
            return new Foo<T> { Value = LHS.Value + RHS.Value; };
        }
    }
    

    I have worked around this issue using .net4 dynamic runtime typing.

    public struct Foo<T>
    {
        public T Value { get; private set; }
    
        public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
        {
            return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
        }
    }
    

    The two things about using dynamic are

    1. Performance. All value types get boxed.
    2. Runtime errors. You "beat" the compiler, but lose type safety. If the generic type doesn't have the operator defined, an exception will be thrown during execution.
    0 讨论(0)
  • 2020-11-21 06:30

    The .NET numeric primitive types do not share any common interface that would allow them to be used for calculations. It would be possible to define your own interfaces (e.g. ISignedWholeNumber) which would perform such operations, define structures which contain a single Int16, Int32, etc. and implement those interfaces, and then have methods which accept generic types constrained to ISignedWholeNumber, but having to convert numeric values to your structure types would likely be a nuisance.

    An alternative approach would be to define static class Int64Converter<T> with a static property bool Available {get;}; and static delegates for Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest). The class constructor could use be hard-coded to load delegates for known types, and possibly use Reflection to test whether type T implements methods with the proper names and signatures (in case it's something like a struct which contains an Int64 and represents a number, but has a custom ToString() method). This approach would lose the advantages associated with compile-time type-checking, but would still manage to avoid boxing operations and each type would only have to be "checked" once. After that, operations associated with that type would be replaced with a delegate dispatch.

    0 讨论(0)
  • 2020-11-21 06:31

    I had a similar situation where I needed to handle numeric types and strings; seems a bit of a bizarre mix but there you go.

    Again, like many people I looked at constraints and came up with a bunch of interfaces that it had to support. However, a) it wasn't 100% watertight and b), anyone new looking at this long list of constraints would be immediately very confused.

    So, my approach was to put all my logic into a generic method with no constraints, but to make that generic method private. I then exposed it with public methods, one explicitly handling the type I wanted to handle - to my mind, the code is clean and explicit, e.g.

    public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
    public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
    public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
    public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);
    
    private static string DoSomethingHelper<T>(this T input, ....)
    {
        // complex logic
    }
    
    0 讨论(0)
  • 2020-11-21 06:33

    There's no constraint for this. It's a real issue for anyone wanting to use generics for numeric calculations.

    I'd go further and say we need

    static bool GenericFunction<T>(T value) 
        where T : operators( +, -, /, * )
    

    Or even

    static bool GenericFunction<T>(T value) 
        where T : Add, Subtract
    

    Unfortunately you only have interfaces, base classes and the keywords struct (must be value-type), class (must be reference type) and new() (must have default constructor)

    You could wrap the number in something else (similar to INullable<T>) like here on codeproject.


    You could apply the restriction at runtime (by reflecting for the operators or checking for types) but that does lose the advantage of having the generic in the first place.

    0 讨论(0)
  • 2020-11-21 06:35

    I was wondering the same as samjudson, why only to integers? and if that is the case, you might want to create a helper class or something like that to hold all the types you want.

    If all you want are integers, don't use a generic, that is not generic; or better yet, reject any other type by checking its type.

    0 讨论(0)
提交回复
热议问题