F # / .NET странность нулевого экземпляра - PullRequest
9 голосов
/ 20 сентября 2010

У меня есть эта C # DLL:

namespace TestCSProject
{
    public class TestClass
    {
        public static TestClass Instance = null;

        public int Add(int a, int b)
        {
            if (this == null)
                Console.WriteLine("this is null");
            return a + b;
        }
    }
}

И это приложение F #, которое ссылается на DLL:

open TestCSProject
printfn "%d" (TestClass.Instance.Add(10,20))

Никто не инициирует статическую переменную Instance.Угадайте, что выводит приложение F #?

this is null
30
Press any key to continue . . .

После нескольких тестов я обнаружил, что если я не использую this (например, для доступа к полю экземпляра), я не получу NullReferenceExpcetion.

Это предполагаемое поведение или пробел в компиляции F # / CLR?

Ответы [ 2 ]

10 голосов
/ 20 сентября 2010

Я подозреваю, что вы обнаружите, что он использует call вместо callvirt, если вы посмотрите на IL.Компилятор C # всегда использует callvirt, даже для не виртуальных методов, потому что он вызывает проверку недействительности.

Это ошибка?Ну, не обязательно.Это зависит от того, что в спецификации языка F # говорится о вызовах методов для нулевых ссылок.Вполне возможно, что в нем говорится, что метод будет вызываться (не виртуально) с нулевой ссылкой «this», что именно и произошло.

C # указывает, что такого рода разыменование вызоветNullReferenceException, но это выбор языка.

Я подозреваю, что подход F # может быть немного быстрее из-за недостатка проверки на нулевое значение ... и не забывайте, что нулевые ссылки менее ожидаемы"в F #, чем в C # ... что может объяснить другой подход, принятый здесь.Или, конечно, это может быть просто недосмотр.

РЕДАКТИРОВАТЬ: я не специалист по чтению спецификации F #, но по крайней мере в разделе 6.9.6 предлагает мне , что этоошибка:

6.9.6 Оценка применения методов

Для сложных применений методов разработанной формой выражения будет либо expr.M (args), либо M (args).

  • (необязательные) выражения и аргументы вычисляются в порядке слева направо, а тело члена оценивается в среде с формальными параметрами, которые сопоставляются с соответствующими значениями аргументов.

  • Если значение expr равно нулю, то возникает исключение NullReferenceException.

  • Если метод представляет собой виртуальный интервал отправки (то есть метод, которыйобъявляется абстрактным), тогда тело члена выбирается в соответствии с картами отправки значения expr.

Независимо от того, считается ли это разработанным приложением или нет, это немного за граньюя, ябред ... но я надеюсь, что это было по крайней мере несколько полезно.

3 голосов
/ 20 сентября 2010

Я думаю, что команда F # считает это поведение ошибкой, которая может измениться в будущем выпуске.

Иногда класс может изменить метод с не виртуального (в выпуске N) на виртуальный (в выпуске N + 1). Это «переломный момент»? Это ломается, если код скомпилирован с использованием исходного класса call вместо callvirt. Понятие «переломное изменение» в основном зависит от градуса , а не вида ; изменение от не виртуального к виртуальному - это «незначительное» изменение. В любом случае, некоторые классы в .NET Framework продемонстрировали такие изменения, и, таким образом, вы столкнетесь с проблемой, аналогичной «хрупкому базовому классу». В подтверждение этого команда F # намеревается изменить коден компилятора для использования callvirt, как это делают C # и VB. Предполагая, что это произойдет, некоторые маловероятные программы, такие как программа в этом репро, могут изменить поведение при компиляции с будущей версией компилятора F #.

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