C#:泛型数学函数(Min,Max等)

Fer*_*ryt 13 c# math

我正在考虑为Min,Max等基本数学运算编写泛型函数.但是我不知道如何比较两种泛型类型:

public T Max<T>(T v1, T v2) where T: struct
{
   return (v1 > v2 ? v1 : v2);
}
Run Code Online (Sandbox Code Playgroud)

那个怎么样?

谢谢.

Joe*_*oey 22

您可能希望限制要实现的泛型类型IComparable:

public T Max<T>(T v1, T v2) where T: struct, IComparable<T>
Run Code Online (Sandbox Code Playgroud)

然后使用CompareTo方法:

{
    return (v1.CompareTo(v2) > 0 ? v1 : v2);
}
Run Code Online (Sandbox Code Playgroud)


Luk*_*keH 22

如果您只想创建比较函数,则可以使用该类型的默认比较器T.例如:

public static T Max<T>(T x, T y)
{
    return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
}
Run Code Online (Sandbox Code Playgroud)

如果是Timplements,IComparable<T>那么将使用该comparer; 如果T没有实现IComparable<T>但是实现IComparable那么将使用该比较器; 如果T没有实现任何一个IComparable<T>,IComparable那么将抛出运行时异常.

如果您希望/需要做的不仅仅是比较项目,那么您可以查看MiscUtil中的泛型运算符实现相关文章.

  • 这是正确的答案,IMO.另外,一个常见的替代模式是有两个重载; 一个将`Comparer <T> .Default`传递给第二个,它接受`IComparer <T>`(对于你想要自定义它的情况).但对于简单的情况,这是要走的路. (3认同)

Fer*_*oni 5

让我不同意。@LukeH 的实现不是 Generic

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

Comparer<T>.Default涉及在运行时检查 T 以确定它是否实现IComparable<T>IComparable或两者都不实现。尽管此行为在http://msdn.microsoft.com/en-us/library/azhsac5f.aspx中没有详细记录,但我们可以推断它,因为Comparer<T>.Default当 T 两者都没有实现时会引发异常。如果检查是在编译时完成的,则不需要异常(运行时),编译时错误就足够了。

然后,当Comparer<T>.Default使用反射时,这意味着运行时的成本很高,那么......,它不是通用的......为什么?

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

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

public static int Max( int x, int y)
{
    return (x.CompareTo(y) > 0) ? x : y;
}
Run Code Online (Sandbox Code Playgroud)

它非常简单,仅涉及比较(或者可能更多,具体取决于 Int32.CompareTo() 的实现方式)。如果我们使用 @LukeH 的实现,我们将向非常简单的东西添加反射。

简而言之:

  1. 总是更喜欢编译时错误而不是运行时异常(这不是 Javascript、Ruby...:-))
  2. 与手写版本相比,此实现效率较低(对于任何类型)

另一方面。 当 x 和 y 相等时,你认为 Max 应该返回什么?

我开始分析 Real-Generic 实现......

理想的实现是这样的......

    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); });
    }
Run Code Online (Sandbox Code Playgroud)

这在 C# 中是不可能的,下一次尝试可能是......

    //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); });
    }
Run Code Online (Sandbox Code Playgroud)

但是,这是不可能的,因为重载解析不考虑泛型约束......

那么,我就会有意识地离开IComparable。我只是担心IComparable<T>

    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); });
    }
Run Code Online (Sandbox Code Playgroud)

  • @FernandoPelliccioni 你的说法并不完全正确。您应该查看 DefaultComparer 的源代码。它非常聪明!它将默认比较器缓存在 Comparer&lt;T&gt; 的静态易失性变量中。因此,您只需支付一次价格,之后它将比(或与) IComparable&lt;T&gt; 的类型转换更快。使用 DefaultComparer 将使函数比“更学术”的 Comparable&lt;T&gt; 解决方案更“通用”。而且您不应该担心运行时异常。编译时检查不能替代单元测试。对您的代码进行单元测试并使用最佳解决方案。 (4认同)
  • 这是关于缺乏编译时类型安全性的一个很好的观点。但如果您要对运行时成本提出主张,我真的希望看到您用一些数字来支持它。我也没有测试过它,但我认为传递函数也会导致一些性能损失。 (2认同)