C#:通用数学函数(最小值、最大值等)

C#:通用数学函数(最小值、最大值等),c#,math,C#,Math,我在考虑为基本的数学运算编写通用函数,如Min、Max等。 但我不知道如何比较两种通用类型: public T Max<T>(T v1, T v2) where T: struct { return (v1 > v2 ? v1 : v2); } public T Max(T v1,T v2),其中T:struct { 返回(v1>v2?v1:v2); } 怎么样 谢谢。您可能希望使用泛型类型来实现: 从内存中,T还需要是i可比较的(将其添加到where),然后使用v1

我在考虑为基本的数学运算编写通用函数,如Min、Max等。 但我不知道如何比较两种通用类型:

public T Max<T>(T v1, T v2) where T: struct
{
   return (v1 > v2 ? v1 : v2);
}
public T Max(T v1,T v2),其中T:struct
{
返回(v1>v2?v1:v2);
}
怎么样

谢谢。

您可能希望使用泛型类型来实现:


从内存中,T还需要是
i可比较的
(将其添加到
where
),然后使用
v1.CompareTo(v2)>0
等。

如果您只想创建比较函数,则可以对类型
T
使用。例如:

public static T Max<T>(T x, T y)
{
    return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
}
publicstatictmax(tx,ty)
{
返回(Comparer.Default.Compare(x,y)>0)?x:y;
}
如果
T
实现,则将使用该比较器;如果
T
没有实现
IComparable
,但实现了,则将使用该比较器;如果
T
未实现
IComparable
IComparable
,则将引发运行时异常


如果您想/需要做的不仅仅是比较项目,那么您可以查看和。

这有点太晚了,但是为什么不使用动态类型和委托作为IComparable的替代方案呢?这样,在大多数情况下,您可以获得编译类型安全性,并且只有当提供的类型都不支持运算符<,并且您无法提供默认比较器作为参数时,才会出现运行时错误

public static T Max<T>(T first, T second, Func<T,T,bool> f = null)
{
    Func<dynamic,dynamic,bool> is_left_smaller = (x, y) => x < y ? true : false;

    var compare = f ?? new Func<T, T, bool>((x, y) => is_left_smaller(x, y));

    return compare(first, second) ? second : first; 
}
public static T Max(T first,T second,Func f=null)
{
Func是左小=(x,y)=>x是左函数还是小函数(x,y));
返回比较(第一,第二)?第二:第一;
}
我不同意。
@LukeH的实现不是通用的

我将解释为什么它不是通用的:

Comparer.Default
涉及在运行时检查T,以确定它是否实现了
IComparable
IComparable
或两者都没有。 虽然在中没有很好地记录此行为,但我们可以将其扣除,因为当T既不实现这两个操作,Comparer.Default也不实现这两个操作时,
Comparer.Default会引发异常。如果检查是在编译时完成的,那么就不需要异常(运行时),只要有编译时错误就足够了

然后,由于
Comparer.Default
使用反射,这意味着运行时成本很高,因此,它不是通用的。。。为什么?

因为泛型编程意味着:单个算法(泛型)可以覆盖许多实现(对于许多类型),保持手写版本的效率

举个例子。整数的手写版本为:

public static int Max( int x, int y)
{
    return (x.CompareTo(y) > 0) ? x : y;
}
它非常简单,只涉及比较(或者更多,具体取决于Int32.CompareTo()的实现方式)。 如果我们使用@LukeH的实现,我们就是在非常简单的东西上添加反射

简言之:

  • 总是喜欢编译时错误而不是运行时异常(这不是Javascript、Ruby、…-)
  • 与手写版本相比,此实现效率较低(对于任何类型)
  • 另一方面。 当x和y相等时,您认为Max应该返回什么?

    我开始分析真正的通用实现

    理想的实现方式是

        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        //Pseudo-code ( note the 'or' next to 'where' )
        public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
    巫术。 可以对任意数量的参数使用Max/Min函数:

    public static T Min<T>(params T[] source) 
        where T: struct, IComparable<T>
    {
        if (source == null) 
            throw new System.ArgumentNullException("source");
    
        T value = default(T);
        bool hasValue = false;
        foreach (T x in source)
        {
            if (hasValue)
            {
                // if (x < value) // https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
                // Less than zero This object precedes the object specified by the CompareTo method in the sort order.
                // Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
                // Greater than zero
                if (x.CompareTo(value) < 0)
                    value = x;
            }
            else
            {
                value = x;
                hasValue = true;
            }
        }
    
        if (hasValue) 
            return value;
    
        throw new System.InvalidOperationException("Sequence contains no elements");
    }
    
    
    public static T Max<T>(params T[] source) 
        where T : struct, IComparable<T>
    {
        if (source == null) 
            throw new System.ArgumentNullException("source");
    
        T value = default(T);
        bool hasValue = false;
        foreach (T x in source)
        {
            if (hasValue)
            {
                // if (x > value) // https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
                // Less than zero This object precedes the object specified by the CompareTo method in the sort order.
                // Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
                // Greater than zero
                if (x.CompareTo(value) > 0)
                    value = x;
            }
            else
            {
                value = x;
                hasValue = true;
            }
        }
    
        if (hasValue) 
            return value;
    
        throw new System.InvalidOperationException("Sequence contains no elements");
    }
    
    publicstatictmin(参数T[]源)
    其中T:struct,i可比较
    {
    if(source==null)
    抛出新的System.ArgumentNullException(“源”);
    T值=默认值(T);
    bool hasValue=false;
    foreach(源中的tx)
    {
    if(hasValue)
    {
    //如果(x<值)//https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
    //小于零此对象位于CompareTo方法按排序顺序指定的对象之前。
    //零此当前实例与CompareTo方法参数指定的对象的排序顺序相同。
    //大于零
    如果(x.CompareTo(值)<0)
    值=x;
    }
    其他的
    {
    值=x;
    hasValue=true;
    }
    }
    if(hasValue)
    返回值;
    抛出新System.InvalidOperationException(“序列不包含元素”);
    }
    公共静态T最大值(参数T[]源)
    其中T:struct,i可比较
    {
    if(source==null)
    抛出新的System.ArgumentNullException(“源”);
    T值=默认值(T);
    bool hasValue=false;
    foreach(源中的tx)
    {
    if(hasValue)
    {
    //如果(x>值)//https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
    //小于零此对象位于CompareTo方法按排序顺序指定的对象之前。
    //零此当前实例与CompareTo方法参数指定的对象的排序顺序相同。
    //大于零
    如果(x.CompareTo(值)>0)
    值=x;
    }
    其他的
    {
    值=x;
    hasValue=true;
    }
    }
    if(hasValue)
    返回值;
    抛出新System.InvalidOperationException(“序列不包含元素”);
    }
    
    我在这个答案上提出的解决方案在被问到这个问题的时候就已经起作用了(实际上我是这样做的)。我很惊讶没有答案会给出这种选择,因此我将给出它

    您可以(当时也可以使用)
    Linq.Expressions
    (2007年添加到.NET3.5中,使之成为问题时的有效答案)

    首先:

    using System.Linq.Expressions;
    
    // ...
    
    public T Max<T>(T v1, T v2)
    {
        var expression = Expression.GreaterThan
            (
                Expression.Constant(v1),
                Expression.Constant(v2)
            );
        return Expression.Lambda<Func<bool>>(expression).Compile()() ? v1 : v2);
    }
    
    好的,为了缓存它,让我们从更简单的泛型类方法开始
        //pseudo-code
        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        public static T Max<T>(T x, T y) where T: IComparable<T>
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
        public static T Max<T>(T x, T y) where T: IComparable
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        public static T Max<T>(T x, T y) where T: IComparable<T>
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
    public static T Min<T>(params T[] source) 
        where T: struct, IComparable<T>
    {
        if (source == null) 
            throw new System.ArgumentNullException("source");
    
        T value = default(T);
        bool hasValue = false;
        foreach (T x in source)
        {
            if (hasValue)
            {
                // if (x < value) // https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
                // Less than zero This object precedes the object specified by the CompareTo method in the sort order.
                // Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
                // Greater than zero
                if (x.CompareTo(value) < 0)
                    value = x;
            }
            else
            {
                value = x;
                hasValue = true;
            }
        }
    
        if (hasValue) 
            return value;
    
        throw new System.InvalidOperationException("Sequence contains no elements");
    }
    
    
    public static T Max<T>(params T[] source) 
        where T : struct, IComparable<T>
    {
        if (source == null) 
            throw new System.ArgumentNullException("source");
    
        T value = default(T);
        bool hasValue = false;
        foreach (T x in source)
        {
            if (hasValue)
            {
                // if (x > value) // https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
                // Less than zero This object precedes the object specified by the CompareTo method in the sort order.
                // Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
                // Greater than zero
                if (x.CompareTo(value) > 0)
                    value = x;
            }
            else
            {
                value = x;
                hasValue = true;
            }
        }
    
        if (hasValue) 
            return value;
    
        throw new System.InvalidOperationException("Sequence contains no elements");
    }
    
    using System.Linq.Expressions;
    
    // ...
    
    public T Max<T>(T v1, T v2)
    {
        var expression = Expression.GreaterThan
            (
                Expression.Constant(v1),
                Expression.Constant(v2)
            );
        return Expression.Lambda<Func<bool>>(expression).Compile()() ? v1 : v2);
    }
    
    using System.Linq.Expressions;
    
    // ...
    
    public T Max<T>(T v1, T v2)
    {
        var a = Expression.Parameter(typeof(int), "a");
        var b = Expression.Parameter(typeof(int), "b");
        var lambda = Expression.Lambda<Func<T, T, bool>>
        (
            Expression.GreaterThan(a, b),
            new[]{a, b}
        );
        return ((Func<T, T, bool>)lambda.Compile())(v1, v2) ? v1 : v2;
    }
    
    using System.Linq.Expressions;
    
    class GenericMath<T>
    {
        private static Func<T, T, bool>? _greaterThan;
        
        public static Func<T, T, bool> GetGreaterThan()
        {
            if (_greaterThan == null)
            {
                var a = Expression.Parameter(typeof(int), "a");
                var b = Expression.Parameter(typeof(int), "b");
                var lambda = Expression.Lambda<Func<T, T, bool>>
                (
                    Expression.GreaterThan(a, b),
                    new[]{a, b}
                );
                _greaterThan = (Func<T, T, bool>)lambda.Compile();
            }
            
            return _greaterThan;
        }
    
        public static T Max(T v1, T v2)
        {
            return GetGreaterThan()(v1, v2) ? v1 : v2;
        }
    }
    
    using System.Linq.Expressions;
    
    class GenericMath
    {
        private readonly static Dictionary<Type, Delegate> _gtCache = new Dictionary<Type, Delegate>();
        
        public static Func<T, T, bool> GetGreaterThan<T>()
        {
            if (!_gtCache.TryGetValue(typeof(T), out var @delegate) || @delegate == null)
            {
                var a = Expression.Parameter(typeof(int), "a");
                var b = Expression.Parameter(typeof(int), "b");
                var lambda = Expression.Lambda<Func<T, T, bool>> 
                (
                    Expression.GreaterThan(a, b),
                    new[]{a, b}
                );
                @delegate = lambda.Compile();
                _addCache[typeof(T)] = @delegate;
            }
            
            return (Func<T, T, bool>)@delegate;
        }
    
        public static T Max<T>(T v1, T v2)
        {
            return GetGreaterThan<T>()(v1, v2) ? v1 : v2;
        }
    }
    
    public sealed class TypeCacheDict<TValue>
    {
        private const int Capacity = 256;
        private readonly Entry[] _entries;
    
        public TypeCacheDict()
        {
            _entries = new Entry[Capacity];
        }
    
        public TValue this[Type key]
        {
            get
            {
                if (TryGetValue(key, out var value))
                {
                    return value;
                }
    
                throw new KeyNotFoundException();
            }
            set => Add(key, value);
        }
    
        public void Add(Type key, TValue value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
    
            var hash = key.GetHashCode();
            var index = hash & (_entries.Length - 1);
            var entry = _entries[index];
            Thread.MemoryBarrier();
            if (entry?.Hash != hash || !entry.Key.Equals(key))
            {
                Interlocked.Exchange(ref _entries[index], new Entry(hash, key, value));
            }
        }
    
        public bool TryGetValue(Type key, out TValue value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
    
            var hash = key.GetHashCode();
            var index = hash & (_entries.Length - 1);
            var entry = _entries[index];
            Thread.MemoryBarrier();
            if (entry?.Hash == hash && entry.Key.Equals(key))
            {
                value = entry.Value;
                return value != null;
            }
    
            value = default;
            return false;
        }
    
        private sealed class Entry
        {
            internal readonly int Hash;
            internal readonly Type Key;
            internal readonly TValue Value;
    
            internal Entry(int hash, Type key, TValue value)
            {
                Hash = hash;
                Key = key;
                Value = value;
            }
        }
    }
    
    private readonly static TypeCacheDict<Delegate> _gtCache = new TypeCacheDict<Delegate>();
    
        public TValue TryGetOrAdd(Type key, Func<TValue> valueFactory)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            
            if (valueFactory == null)
            {
                throw new ArgumentNullException(nameof(valueFactory));
            }
    
            var hash = key.GetHashCode();
            var index = hash & (_entries.Length - 1);
            var entry = _entries[index];
            Thread.MemoryBarrier();
            if (entry?.Hash == hash && entry.Key.Equals(key))
            {
                return entry.Value;
            }
            
            var value = valueFactory();
            Interlocked.Exchange(ref _entries[index], new Entry(hash, key, value));
            return value;
        }
    
        public static Func<T, T, bool> GetGreaterThan<T>()
        {
            return (Func<T, T, bool>)_gtCache.TryGetOrAdd
            (
                typeof(T),
                ()=>
                {
                    var a = Expression.Parameter(typeof(int), "a");
                    var b = Expression.Parameter(typeof(int), "b");
                    var lambda = Expression.Lambda<Func<T, T, bool>>(Expression.GreaterThan(a, b), new[]{a, b});
                    return lambda.Compile();
                }
            );
        }
    
    Console.WriteLine(GenericMath.Max<int>(90, 100)); // 100
    
        private readonly static TypeCacheDict<Delegate> _addCache = new TypeCacheDict<Delegate>();
        
        public static Func<T, T, T> GetAdd<T>()
        {
            return (Func<T, T, T>)_addCache.TryGetOrAdd
            (
                typeof(T),
                ()=>
                {
                    var a = Expression.Parameter(typeof(int), "a");
                    var b = Expression.Parameter(typeof(int), "b");
                    var lambda = Expression.Lambda<Func<T, T, T>>(Expression.Add(a,b), new[]{a, b});
                    return lambda.Compile();
                }
            );
        }
    
        public static T Add<T>(T v1, T v2)
        {
            return GetAdd<T>()(v1, v2);
        }
    
    Console.WriteLine(GenericMath.Add<int>(90, 100)); // 190