Странное поведение от разрешения перегрузки функции в c # с дженериками [duplicate]

NullPointerException s - исключения, возникающие при попытке использовать ссылку, которая указывает на отсутствие местоположения в памяти (null), как если бы она ссылалась на объект. Вызов метода по нулевой ссылке или попытка получить доступ к полю нулевой ссылки вызовет функцию NullPointerException. Они наиболее распространены, но другие способы перечислены на странице NullPointerException javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации NullPointerException, be:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

В первой строке внутри main я явно устанавливаю ссылку Object obj равной null. Это означает, что у меня есть ссылка, но она не указывает на какой-либо объект. После этого я пытаюсь обработать ссылку так, как если бы она указывала на объект, вызывая метод на нем. Это приводит к NullPointerException, потому что нет кода для выполнения в местоположении, на которое указывает ссылка.

(Это техничность, но я думаю, что она упоминает: ссылка, которая указывает на null, равна 't то же, что и указатель C, указывающий на недопустимую ячейку памяти. Нулевой указатель буквально не указывает на в любом месте , который отличается от указаний на местоположение, которое оказывается недопустимым.)

5
задан nvoigt 22 April 2014 в 12:00
поделиться

3 ответа

Разрешение метода говорит, что лучше ближе . См. Сообщение в блоге для точных правил.

Что означает более близкое? Компилятор увидит, может ли он найти точное совпадение, если он не может найти по какой-либо причине, он найдет следующие возможные совместимые методы и т. Д.

Давайте сначала сделаем этот метод компиляцией, удалив ограничение SomeInterface .

public static class ExtensionMethods
{
    public static void Method<T>(this T parameter) //where T : SomeInterface
    { }

    public static void Method<T>(this IEnumerable<T> parameter) //where T : SomeInterface 
    { }
}

Теперь компилятор с удовольствием компилирует и замечает, что оба вызова метода идут до Method(T), а не Method(IEnumerable<T>). Почему?

Поскольку Method(T) ближе в том смысле, что может принимать любой тип в качестве параметра, а также не требует никакого преобразования.

Почему Method(IEnumerable<T>) не ближе?

Это потому, что у вас есть тип времени компиляции переменной как List<T>, поэтому для него требуется преобразование ссылок от List<T> до IEnumerable<T>. Который ближе, но не делает никаких конверсий вообще.

Вернемся к вашему вопросу.

Почему instances.Method(); не компилируется?

Опять же, как было сказано ранее для использования Method(IEnumerable<T>), нам нужно какое-то ссылочное преобразование, так что очевидно, что это не ближе. Теперь у нас остается только один метод, который очень близок Method<T>. Но проблема в том, что вы ограничили его с помощью SomeInterface, и, очевидно, List<SomeImplementation>() не конвертируется в SomeInterface.

Проблема (угадывание) проверка общих ограничений происходит после того, как компилятор выбирает более близкую перегрузки. Это делает недействительной выбранную лучшую перегрузку в этом случае.

Вы можете легко исправить это, изменив статический тип переменной на IEnumerable<SomeImplementation>, который будет работать, и теперь вы знаете, почему.

IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();
6
ответ дан Sriram Sakthivel 19 August 2018 в 15:43
поделиться
  • 1
    Хорошо, это на самом деле не решает мою проблему (я все равно должен дать другое имя, чтобы люди могли использовать его без сюрпризов), но по крайней мере это довольно хорошее объяснение why . – nvoigt 29 August 2014 в 10:11

Вы пытались реализовать первый без дженериков, так как он должен вести себя одинаково:

public static void Method(this SomeInterface parameter) { /*...*/ }

Или, как предложил Дмитрий, путем вызова второго из следующих способов:

instances.Method<SomeImplementation>();

Но здесь вам нужно добавить <SomeImplementation> к каждому вызову ...

1
ответ дан ChrFin 19 August 2018 в 15:43
поделиться
  • 1
    Это хорошая идея. Возможно, мне следовало расширить свой пример. В нашем реальном проекте T ограничено использование двух различных интерфейсов, поэтому мы используем дженерики. – nvoigt 22 April 2014 в 12:05
  • 2
    Ах хорошо. В этом случае моя идея не работает (но будет работать для этого случая - просто протестировала ее) ... – ChrFin 22 April 2014 в 12:06
  • 3
    Я пробовал много вещей и не мог заставить его работать с дженериками в обоих случаях без дополнительного кода, вызывающего метод, и IMO он должен работать - возможно, кто-то еще может пролить свет на это поведение (или это может быть ошибка в разрешении метода) ... – ChrFin 22 April 2014 в 12:32
  1. Хотя я знаю, что вы этого не хотите, я думаю, вы должны действительно подумать, должны ли имена методов быть одинаковыми. Я не вижу, как одно и то же имя может воздействовать на экземпляр и собирать такие экземпляры. Например, если ваше имя метода Shoot для T, то другой метод должен звучать как ShootThemAll или что-то подобное.
  2. Или иначе вы должны сделать свое назначение немного другим:
    IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();
    instances.Method(); //now this should work
    
  3. Как последний вариант, как говорит Димитрий в комментариях, вы должны явно указать аргумент типа.
    instances.Method<SomeImplementation>();
    
1
ответ дан nawfal 19 August 2018 в 15:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: