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

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

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64
  • I'm aware of the where keyword, but can't find an interface for only these types,

    Something like:

    static bool IntegerFunction<T>(T value) where T : INumeric 
    

    Hejlsberg has described the reasons for not implementing the feature in an interview with Bruce Eckel.

    I have to admit, though, that I don't know how he thinks his proposed workaround will work. His proposal is to defer arithmetic operations to some other generic class (read the interview!). How does this help? IMHO, not much.


    Considering the popularity of this question and the interest behind such a function I am surprised to see that there is no answer involving T4 yet.

    In this sample code I will demonstrate a very simple example of how you can use the powerful templating engine to do what the compiler pretty much does behind the scenes with generics.

    Instead of going through hoops and sacrificing compile-time certainty you can simply generate the function you want for every type you like and use that accordingly (at compile time!).

    In order to do this:

  • Create a new Text Template file called GenericNumberMethodTemplate.tt.
  • Remove the auto-generated code (you'll keep most of it, but some isn't needed).
  • Add the following snippet:
  • <#@ template language="C#" #>
    <#@ output extension=".cs" #>
    <#@ assembly name="System.Core" #>
    
    <# Type[] types = new[] {
        typeof(Int16), typeof(Int32), typeof(Int64),
        typeof(UInt16), typeof(UInt32), typeof(UInt64)
        };
    #>
    
    using System;
    public static class MaxMath {
        <# foreach (var type in types) { 
        #>
            public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
                return val1 > val2 ? val1 : val2;
            }
        <#
        } #>
    }
    

    That's it. You're done now.

    Saving this file will automatically compile it to this source file:

    using System;
    public static class MaxMath {
        public static Int16 Max (Int16 val1, Int16 val2) {
            return val1 > val2 ? val1 : val2;
        }
        public static Int32 Max (Int32 val1, Int32 val2) {
            return val1 > val2 ? val1 : val2;
        }
        public static Int64 Max (Int64 val1, Int64 val2) {
            return val1 > val2 ? val1 : val2;
        }
        public static UInt16 Max (UInt16 val1, UInt16 val2) {
            return val1 > val2 ? val1 : val2;
        }
        public static UInt32 Max (UInt32 val1, UInt32 val2) {
            return val1 > val2 ? val1 : val2;
        }
        public static UInt64 Max (UInt64 val1, UInt64 val2) {
            return val1 > val2 ? val1 : val2;
        }
    }
    

    In your main method you can verify that you have compile-time certainty:

    namespace TTTTTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                long val1 = 5L;
                long val2 = 10L;
                Console.WriteLine(MaxMath.Max(val1, val2));
                Console.Read();
            }
        }
    }
    

    I'll get ahead of one remark: no, this is not a violation of the DRY principle. The DRY principle is there to prevent people from duplicating code in multiple places that would cause the application to become hard to maintain.

    This is not at all the case here: if you want a change then you can just change the template (a single source for all your generation!) and it's done.

    In order to use it with your own custom definitions, add a namespace declaration (make sure it's the same one as the one where you'll define your own implementation) to your generated code and mark the class as partial . Afterwards, add these lines to your template file so it will be included in the eventual compilation:

    <#@ import namespace="TheNameSpaceYouWillUse" #>
    <#@ assembly name="$(TargetPath)" #>
    

    Let's be honest: This is pretty cool.

    Disclaimer: this sample has been heavily influenced by Metaprogramming in .NET by Kevin Hazzard and Jason Bock, Manning Publications.


    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.

    链接地址: http://www.djcxy.com/p/33204.html

    上一篇: 哈斯克尔:哪里与让

    下一篇: 是否有一个约束将我的泛型方法限制为数字类型?