【问题标题】:How can I make this simple method into a generic method?我怎样才能把这个简单的方法变成一个通用的方法?
【发布时间】:2020-04-19 09:54:22
【问题描述】:

我有一个方法如下:

int coerce(int val, int min=0, int max = 10)
{
    if (val < min)
        return min;
    if (val > max)
        return max;
    return val;
}

现在,我必须为byte、float、double 和其他数字类型制作它。

众所周知,为这些类型制作大量类似的方法是非常无效的,所以我想将它变成一个泛型方法。以下是我尝试做的:

T coerce<T>(T val, T min=(T)0, T max=(T)10) where T:IComparable
{
    // ... same as the above ...
}

我知道代码没有运行,这就是我要求这样做的原因。我目前对两个问题感到困惑:

  1. 如何比较T 类型?

    Visual Studio 警告运算符 &lt; 和 &gt;。我尝试使用where T:IComparable,但它并没有解决我的问题。

  2. 如何为泛型参数设置默认值?

    我尝试使用(T)0 和(T)10。但无论如何,这不是一个好的选择。

这可能是一个简单的问题,但我无法从 Google 找到答案。

【问题讨论】:

  • 你看过msdn的例子了吗? docs.microsoft.com/en-us/dotnet/api/…
  • IComparable 定义CompareTo,而不是比较运算符。因此,例如,您需要使用 val.CompareTo(min) == -1 而不是 val &lt; min
  • ... 你不能使用int 为你的通用实现默认值,你能做的最好是int min = default 但IMO你不应该尝试在这个方法中实现默认值。跨度>
  • 如何为泛型类型参数设置默认值? - 您可以使用Default(T) 将其设置为数据类型的默认值。在你的情况下,0 代表整数。
  • 我认为Clamp(),而不是coerce(),将是这个方法更合适和知名的名称。考虑到这一点,Where can I find the “clamp” function in .NET? 的许多答案都提供了一个通用的实现。

标签: c# .net generics comparison-operators


【解决方案1】:

您应该使用Compare 方法而不是&lt; 或&gt; 运算符。并应用正确的通用约束,对于数字类型,它应该是IComparable, IComparable&lt;T&gt;, IConvertible, IEquatable&lt;T&gt;, IFormattable。但是,您只能留下where T : struct, IComparable&lt;T&gt;,它应该足以满足您的目的(但struct 在这里很重要,因为您正在比较值类型)

T coerce<T>(T val, T min, T max) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
{
    if (val.CompareTo(min) < 0)
        return min;
    if (val.CompareTo(max) > 0)
        return max;

    return val;
}

您还可以指定默认的min 值,例如T min = default(T),但不能为max 值这样做。

按照 cmets,如果将 Nullable&lt;T&gt; 用于 min 和 max 值,则代码可以这样编写

T coerce<T>(T val, T? min = default, T? max = default) where T : struct, IComparable<T>
{
    var minValue = min.HasValue ? min.Value : default(T);
    var maxValue = max.HasValue ? max.Value : (T)Convert.ChangeType(10, typeof(T));

    if (val.CompareTo(minValue) < 0)
        return minValue;
    if (val.CompareTo(minValue) > 0)
        return maxValue;

    return val;
}

【讨论】:

  • 感谢您的帮助。 Q1 现在对我来说非常清楚。但是,Q2 仍然是个问题。min 不能为 0,所以我不能使用 default(T) 上面的代码只是我的一个简化示例。无论如何,谢谢你的帮助!!
  • @J.-H.Bak 可以使用反射来获取Min 和Max 的值T,在这个answer 中有解释,但看起来有点矫枉过正在这里,海事组织。直接指定显式值更清晰易读
  • 感谢您的评论。我同意这是一种浪费。在这里,我带来了一个新的想法,为输入参数分配默认值。但是还需要一步。我可以再请求一个帮助来完成这个想法吗? T coerce&lt;T&gt;(T val, T? min, T? max) where T:struct ,然后是 if(max == null) max = (T)10。但现在我无法为其赋值。
  • @J.-H.Bak T 可以是任何数值,而不仅仅是int,所以很遗憾,max = (T)10 无效。您可以为此使用Convert.ChangeType。如果T 可以为空,您需要访问它的Value 属性
【解决方案2】:
public T Max<T>(T val, T min, T max) where T : IComparable<T>
{
    if (val.CompareTo(min) == -1)
        return min;

    if (val.CompareTo(min) == 1)
        return max;

    return val;
}

Detail

【讨论】:

  • 感谢您的宝贵帮助。 :) 现在我意识到我完全误解了IComparable 的东西。
【解决方案3】:

诀窍是使用 IComparable 方法,而不是比较运算符。在这种情况下,CompareTo 方法

参数的默认值并不容易,您假设数值并进行直接转换,但是 T 可以是任何 IComparable 数据类型

public static T Coerce<T>(T val, T min, T max) where T:IComparable {
    if (val.CompareTo(min) < 0)
        return min;
    else if (val.CompareTo(max) > 0)
        return max;
    else
        return val;
}

小提琴:https://dotnetfiddle.net/fJUDdR

【讨论】:

  • 感谢您的友好回答并为此分享您的时间。这对我帮助很大!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
相关资源
最近更新 更多