C# 函数的一般约束

C# 函数的一般约束,c#,generics,type-constraints,C#,Generics,Type Constraints,我想写一个对类型有约束的泛型函数。我特别想要这样的东西: bool IsInList<T>(T value, params T[] args) { bool found = false; foreach(var arg in args) { if(arg == value) { found = true; break; } } return foun

我想写一个对类型有约束的泛型函数。我特别想要这样的东西:

bool IsInList<T>(T value, params T[] args)
{
    bool found = false;
    foreach(var arg in args)
    {
        if(arg == value)
        {
            found = true;
            break;
        }
    }
    return found;
 }

然而,编译器在相等线上发出嘎嘎声。所以我想对它实现的IEquatable类型加上一个约束。然而,约束似乎只在类级别起作用。这是正确的,还是有某种方法可以通用地指定它?

通常,“==”运算符仅用于不可变类型—(内置值类型或自定义不可变类型重载了==运算符。除非重载它,否则如果在引用类型上使用它,(字符串除外)只有当两个变量都指向同一类型的实例(同一对象)时,它才会返回true。如果两个veriable都指向该类型的不同实例,即使它们都具有相同的内部数据值,它也会返回false

因此,您需要将类型T限制为那些实现
Equals()
函数的类型,该函数用于确定任何类型的两个实例是否“相等”,而不仅仅是它们都指向同一实例……并使用该函数。 内置接口
IEquatable
为您表达了这一点。(有点像
IComparable
要求类型具有
CompareTo()
函数)
此外,你可以使你的功能更加简洁和清晰

试试这个:

bool IsInList<T>(T value, params T[] args) where T:IEquatable<T>
{ 
    foreach(var arg in args)          
        if(value.Equals(arg)) return true;
    return false;
 } 
bool IsInList(T值,参数T[]args),其中T:IEquatable
{ 
foreach(args中的变量arg)
if(value.Equals(arg))返回true;
返回false;
} 
您还应该理解Equals()和==之间的区别,并决定哪一个更适合您的意图……查看参考以了解更多信息。

只需替换即可

arg == value


通用约束也适用于通用方法:

bool IsInList<T>(T value, params T[] args) where T : IEquatable<T>
bool IsInList(T值,参数T[]args),其中T:IEquatable
但是,
IEquatable
没有定义
运算符==
,只定义
等于(t)

因此,您应该使用
Equals()
,甚至不需要约束:
Equals(object)
object
的成员


另外,不要忘记,如果对象为
null
Equals
将不起作用,其他人提到了
IEquatable
,这当然是一个很好的潜在约束

另一个要记住的选项是
EqualityComparer.Default
,如果可用,它将使用
IEquatable
,但如果不可用,则返回到
object.Equals(object)
。这意味着您可以将其用于早于泛型但覆盖
Equals
的类型,例如:

bool IsInList<T>(T value, params T[] args)
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
    bool found = false;
    foreach(var arg in args)
    {
        if(comparer.Equals(arg, value))
        {
            found = true;
            break;
        }
    }
    return found;
}
  • LINQ已经包含了用于此的方法,
    包含
    ,因此您可以将代码简化为:

    bool IsInList<T>(T value, params T[] args)
    {
        return args.Contains(value);
    }
    
  • 鉴于
    args
    是一个数组,而不是
    列表,您的方法可能有点命名错误


  • 这不起作用,
    IEquatable
    不包含
    运算符==
    ,它也不是以某种方式神奇地创建的。而且,
    Equals()
    即使没有
    IEquatable
    (尽管它是
    等于(对象)
    而不是
    等于(t)
    )。是的,但您不能期望编译器在您定义的任意自定义类型上为您实现Equals方法的代码。您必须自己编写该代码。此约束仅限制您在未实现此方法的类型上调用该方法。而且,是的,您的==运算符为否也是正确的在IEquatable中我的错误,我编辑了以更正…查看我添加的链接以了解更多信息。不,这不完全是同一件事。
    IEquatable.Equals
    的签名与
    object.Equals
    的签名不同。通过这种方式约束它,你阻止它在默认相等比较完美的类型上工作很好。很遗憾,基本上没有办法说“将T约束到具有自定义相等比较的类型”-您当前的约束太窄,“无约束”可以说太宽。+1是非常详细的答案,但我认为您的第一个代码中有一个错误:if(comparer.Equals(arg==value))-我认为应该是if(comparer.Equals(arg,value))。@ShdNx:Whoops,绝对:)不是
    IndexOf
    只是
    System.Array
    上的一个静态方法。你可以使用
    object.Equals(a,b)
    而不是
    a.Equals(b)
    ,这样您就不必担心空对象。现在,如果MS能够更新他们的文档以反映这一现实,而不是给每个人留下约束仅对类声明有效的印象,那就好了。
    bool IsInList<T>(T value, params T[] args)
    {
        IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
        bool found = false;
        foreach(var arg in args)
        {
            if(comparer.Equals(arg, value))
            {
                found = true;
                break;
            }
        }
        return found;
    }
    
    bool IsInList<T>(T value, params T[] args)
    {
        IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
        foreach (var arg in args)
        {
            if (comparer.Equals(arg, value))
            {
                return true;
            }
        }
        return false;
    }
    
    bool IsInList<T>(T value, params T[] args)
    {
        return args.Contains(value);
    }
    
    bool IsInList<T>(T value, params T[] args)
    {
        return Array.IndexOf(args, value) != -1;
    }