关于函数的通用约束

时间:2010-09-25 17:00:43

标签: 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 found;
 }

关键是您可以检查项目是否在参数列表中:

if(IsInList("Eggs", "Cheese", "Eggs", "Ham"))

然而,编译器在平等线上呱呱叫。所以我想对它实现IEquatable的类型进行约束。但是,限制似乎只在班级有效。这是正确的,还是有某种方法来指定这一点?

4 个答案:

答案 0 :(得分:11)

其他人提到IEquatable<T>这肯定是一个很好的潜在约束。

要记住的另一个选项是EqualityComparer<T>.Default,如果可用,将使用IEquatable<T>,否则将回退到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;
}

请注意,默认的相等比较器也可以处理空引用,因此您无需担心这些问题。如果类型T未覆盖object.Equals(object)或已实现IEquatable<T>,您将获得引用相等语义(即,如果确切引用位于数组中,它将仅返回true )。

其他几点:

  • 您希望坚持单一退出点,使代码的可读性降低,IMO。这里不需要额外的变量:

    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;
    }
    
  • LINQ已经包含了一个方法Contains,因此您可以将代码简化为:

    bool IsInList<T>(T value, params T[] args)
    {
        return args.Contains(value);
    }
    
  • Array也有效地包含此功能,IndexOf

    bool IsInList<T>(T value, params T[] args)
    {
        return Array.IndexOf(args, value) != -1;
    }
    
  • 鉴于args是一个数组,而不是List<T>,您的方法可能会有一点误导性地命名。

答案 1 :(得分:9)

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

bool IsInList<T>(T value, params T[] args) where T : IEquatable<T>

但是,IEquatable<T>未定义operator ==,仅定义Equals(T)

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

另请注意,如果对象为Equalsnull将无效。

答案 2 :(得分:1)

`=='运算符通常仅用于不可变类型 - (内置值类型或重载==运算符的自定义不可变类型。除非你重载它,否则在引用类型上使用它,(除了如果两个变量都指向同一个类型的实例(同一个对象),它只返回true。如果两个变量指向该类型的不同实例,即使它们都具有所有相同的内部数据值,它也会返回false。 / p>

所以你需要将类型T限制为那些实现Equals()函数的类型,这个函数用于确定任何类型的两个实例是否“相等”,而不仅仅是它们都指向同一个实例......而是用它来代替。 内置接口IEquatable<T>为您表达了这一点。 (有点像IComparable<T>要求类型具有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;
 } 

您还应该了解Equals()和==之间的区别,并确定哪个更适合您的意图...查看this参考了解更多信息。

答案 3 :(得分:0)

只需替换

arg == value

arg.Equals(value)