Метод расширения и метод члена: почему каждый реализуется по-разному компиляторами (внутренне)? - PullRequest
5 голосов
/ 03 сентября 2011

Рассмотрим этот код:

 A a = null;
 a.f(); //Will it throw NullReferenceException?

Вышеуказанный бросок NullReferenceException?

Ответ: это зависит от того, что f().

Это различие приводит к вопросу: как каждый тип метода реализуется и просматривается компиляторами C #? Кроме того, почему метод-член должен выдавать исключение , даже если он не обращается ни к каким данным ? Кажется, что компилятор C # заранее предполагает, что метод-член получит доступ к данным-членам, и поэтому он выдает исключение, если объект имеет значение null, поскольку использование данных-членов с нулевым объектом недоступно. Однако в случае метода расширения он откладывает это решение до тех пор, пока на самом деле не попытается получить доступ к данным члена, используя нулевую ссылку, только тогда он выдаст исключение .

Насколько мое понимание верно? И если это так, то почему такая разница?

Да, я знаю, что если f() является методом расширения, то a.f() эквивалентно записи AExt.f(a), поэтому последний не должен выдавать исключение, пока a не будет использован для доступа к члену. Но я сосредоточен в основном на реализациях компилятора (которые могут реализовывать даже методы-члены таким же образом).

Ответы [ 2 ]

2 голосов
/ 03 сентября 2011

Да, именно так ведет себя этот код (если метод расширения не проверяет null и выдает исключение сам по себе). Вы правы, что вызов не виртуального метода экземпляра в null может сработать, если этот метод не имеет доступа к полям экземпляра класса (прямо или косвенно).

Но разработчики языка посчитали, что это может сбить с толку , поэтому они проверяют, что объект не null, используя инструкцию callvirt IL.

С методами расширения это не так сложно (никто не ожидает, что this будет null, но аргумент, объявленный this IEnumerable<TSource> source, должен быть проверен). И вы можете вызывать метод расширения как обычный статический метод, так что в любом случае он должен проверять null. Кроме того, оба способа вызова функции, вероятно, должны вести себя одинаково.

1 голос
/ 03 сентября 2011

Методы экземпляра должны выдавать NullReferenceException, когда объект имеет значение null, поскольку они по своей природе гарантируют, что объект доступен, когда этот метод выполняется.Думайте о методах экземпляра как о действии, которое выполняет экземпляр объекта, и когда сам объект не существует, действие даже не может быть вызвано.Методы расширения являются просто синтаксическим сахаром, и компилятор немедленно переводит их, как вы упомянули выше AExt.f(a).Это всего лишь удобство , и оно помогает вам лучше читать код.

Для случаев, когда вы хотите вызывать метод независимо от того, доступен экземпляр или нетязык предоставляет статические методы в качестве функции.Как вы можете сказать, методы расширения являются статическими.

...