Почему IsNan является статическим методом в классе Double вместо свойства экземпляра? - PullRequest
12 голосов
/ 16 декабря 2008

Вопрос в названии, почему:

return double.IsNaN(0.6d) && double.IsNaN(x);

Вместо

return (0.6d).IsNaN && x.IsNaN;

Я спрашиваю, потому что при реализации пользовательских структур, которые имеют специальное значение с тем же значением, что и NaN, я предпочитаю второе.

Кроме того, производительность свойства обычно лучше, так как при этом не требуется копировать структуру в стеке для вызова статического метода IsNaN (а поскольку мое свойство не виртуально, риск автобоксирования отсутствует). Конечно, это не проблема для встроенных типов, поскольку JIT может оптимизировать этот процесс легко.

Мое лучшее предположение на данный момент заключается в том, что, поскольку у вас не может быть свойства и статического метода с одинаковым именем в двойном классе, они предпочитают синтаксис, основанный на Java. (На самом деле вы могли бы иметь и то и другое, одно из которых определяет метод получения свойства get_IsNaN, а другое - статический метод IsNaN, но это может привести к путанице в любом языке .Net, поддерживающем синтаксис свойства)

Ответы [ 7 ]

14 голосов
/ 16 декабря 2008

Статические методы являются поточно-ориентированными, методы на примитивах, как правило, должны быть поточно-безопасными, чтобы поддерживать многопоточность в платформе (имеется в виду, по крайней мере, в безопасности от внутренних условий гонки), методы экземпляра принимают управляемый указатель на структуру, что означает, что структура / примитив может быть изменен одновременно во время выполнения метода, с другой стороны, статические методы берут копию структуры / примитива и, следовательно, защищены от условий многопоточности.

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

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

Редактировать: @VirtualBlackFox Я подготовил и пример, чтобы показать, что методы экземпляра в структурах не являются потокобезопасными даже для неизменных структур:

using System;
using System.Threading;

namespace CA64213434234
{
    class Program 
    {
        static void Main(string[] args)
        {
            ManualResetEvent ev = new ManualResetEvent(false);
            Foo bar = new Foo(0);
            Action a =  () => bar.Display(ev);
            IAsyncResult ar = a.BeginInvoke(null, null);
            ev.WaitOne();
            bar = new Foo(5);
            ar.AsyncWaitHandle.WaitOne();
        }
    }

    public struct Foo
    {
        private readonly int val;
        public Foo(int value)
        {
            val = value;
        }
        public void Display(ManualResetEvent ev)
        {
            Console.WriteLine(val);
            ev.Set();
            Thread.Sleep(2000);
            Console.WriteLine(val);
        }
    }
}

Метод экземпляра на дисплее выводит на печать: 0 5 * +1010 *

даже если структура неизменна. Для потоковых методов используйте статические методы.

9 голосов
/ 16 декабря 2008

интересный вопрос; не знаю ответа - но если это действительно вас беспокоит, вы можете объявить метод расширения, но он все равно будет использовать стек и т. д.

static bool IsNaN(this double value)
{
    return double.IsNaN(value);
}

static void Main()
{
    double x = 123.4;
    bool isNan = x.IsNaN();
}

Было бы лучше (по синтаксису), если бы C # имел свойства расширения, но вышеизложенное примерно соответствует тому, что вы можете получить в данный момент, но в любом случае он должен "встраиваться" довольно хорошо.


Update; думая об этом, есть еще одно различие между статическим и экземпляром; C # всегда вызывает методы экземпляра с "callvirt", а не с "call", даже если тип запечатан как необнуляемый. Так что, возможно, выигрыш в производительности от статичности? К счастью, методы расширения по-прежнему считаются статическими, поэтому вы можете сохранить это поведение.

4 голосов
/ 17 декабря 2008

@ Поп Каталин: Я не согласен с тем, что ты сказал:

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

Вот небольшая программа, демонстрирующая, что статические методы не решают эту проблему для структур:

using System;
using System.Threading;
using System.Diagnostics;

namespace ThreadTest
{
    class Program
    {
        struct SmallMatrix
        {
            double m_a, m_b, m_c, m_d;

            public SmallMatrix(double x)
            {
                m_a = x;
                m_b = x;
                m_c = x;
                m_d = x;
            }

            public static bool SameValueEverywhere(SmallMatrix m)
            {
                return (m.m_a == m.m_b)
                    && (m.m_a == m.m_c)
                    && (m.m_a == m.m_d);
            }
        }

        static SmallMatrix s_smallMatrix;

        static void Watcher()
        {
            while (true)
                Debug.Assert(SmallMatrix.SameValueEverywhere(s_smallMatrix));
        }

        static void Main(string[] args)
        {
            (new Thread(Watcher)).Start();
            while (true)
            {
                s_smallMatrix = new SmallMatrix(0);
                s_smallMatrix = new SmallMatrix(1);
            }
        }
    }
}

Обратите внимание, что такого поведения нельзя наблюдать с двойными значениями на общем процессоре, так как большинство инструкций x86 имеют версию, работающую с кусками 64 бита, такими как movl.

Таким образом, безопасность потоков не является хорошей причиной для статичности IsNaN:

  1. Фреймворк должен быть независимым от платформы и поэтому не должен предполагать такие вещи, как архитектура процессора. Потокобезопасность IsNaN зависит от того факта, что 64-битные значения всегда доступны и изменяются атомарно на целевой архитектуре (А целевые платформы Compact не x86 ...).
  2. IsNaN сам по себе бесполезен, и в контексте, когда несколько потоков могут получить к нему доступ someVar этот код в любом случае небезопасен (независимо от безопасности потока IsNaN):
print("code sample");
if (!double.IsNaN(someVar))
    Console.WriteLine(someVar);

Я имею в виду, что даже если IsNaN реализован путем == сравнения со всеми возможными значениями NaN ... (на самом деле это невозможно) ... кому важно, чтобы значение эволюционировало во время выполнения метода, если в любом случае оно могло бы измениться после завершения метода ... или это могло быть даже промежуточное значение, которого никогда не должно было быть здесь, если целевая архитектура не x86 ...

Доступ к интристическим значениям в двух разных потоках НЕ в целом безопасен, поэтому я не вижу интереса в обеспечении некоторой иллюзии безопасности, применяя какой-либо статический метод при работе со структурами или любым другим типом,

0 голосов
/ 19 ноября 2011

Double.IsNan следует той же схеме, что и String.IsNullorEmpty. Последний ведет себя так же, как и раньше, потому что, к сожалению, нет способа объявить, что не виртуальный метод экземпляра должен использоваться с нулевым «this». Хотя такое поведение может быть странным для изменяемых ссылочных типов, оно будет очень полезно для вещей, которые должны быть ссылочными типами, но которые должны вести себя семантически, как неизменяемые значения. Например, тип «String» был бы намного удобнее, если бы вызов его свойств для нулевого объекта вел себя идентично вызову их для пустой строки. На самом деле, существует странная смесь контекстов, в которой объект с нулевой строкой будет рассматриваться как пустая строка, а те, в которых попытка использовать один вызовет ошибку. Было бы намного чище, если бы строка вела себя как тип значения, который инициализируется пустой строкой.

0 голосов
/ 17 декабря 2008

Я думаю, что Марк был на ответ.

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

0 голосов
/ 16 декабря 2008

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

Я действительно не знаю почему, и не думал об этом сзади, но логически это кажется хорошим. Все равно интересуюсь ответом; -)

0 голосов
/ 16 декабря 2008

Различие между экземпляром и static является фундаментальной точкой, которую язык C # (и, как вы говорите, Java) решает прояснить (в C ++ вы можете вызывать метод static через экземпляр, но это просто синтаксис - под капотом instance.StaticX - это то же самое выражение экземпляра Class.StaticX.

Хотя переход к свободным интерфейсам начал многое из этого разгадывать ...

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