Общие ограничения на функции - PullRequest
9 голосов
/ 25 сентября 2010

Я хочу написать обобщенную функцию, которая имеет ограничение на тип.В частности, я хочу что-то вроде этого:

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 ]

11 голосов
/ 25 сентября 2010

Другие упоминали 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 только если точная ссылка находится в массиве).

Несколько других моментов:

  • Ваше желание придерживаться единой точки выхода делает код менее читабельным, ИМО. Здесь нет необходимости в дополнительной переменной:

    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;
    }
    
    1036 **
  • Ваш метод, возможно, несколько неверно назван, учитывая, что args - это массив, а не List<T>.

9 голосов
/ 25 сентября 2010

Общие ограничения работают и на общие методы:

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

НО, IEquatable<T> не определяет operator ==, только Equals(T).

Итак, вы должны использоватьEquals() и вам даже не нужно ограничение для этого: Equals(object) является членом object.

Также не забывайте, что Equals не будет работать, если объект null.

1 голос
/ 25 сентября 2010

Оператор `== 'обычно используется только для неизменяемых типов - (встроенные типы значений или пользовательские неизменяемые типы, которые перегружают оператор ==. Если вы не перегружаете его, если используете его на ссылочных типах, (кроме strings) возвращает значение true, только если обе переменные указывают на один и тот же экземпляр типа (один и тот же объект), и возвращает false, если две проверяемые переменные указывают на разные экземпляры типа, даже если они обе имеют одинаковые значения внутренних данных.

Таким образом, вам нужно ограничить тип 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 () и == и решать, что больше подходит для ваших целей ... Проверьте этот справочник для получения дополнительной информации.

0 голосов
/ 25 сентября 2010

Просто замените

arg == value

с

arg.Equals(value)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...