• typeof(T) vs. TypeOf⟨T⟩
    –1
    Считайте всё это, чем захотите, я вам уже приводил своё определение паттерна раньше, и оно расходится с вашим, насколько мы выяснили. Поэтому нет ничего удивительного, что наши мнения снова отличаются.
  • typeof(T) vs. TypeOf⟨T⟩
    –1
    На этом ваша аналогия и развалилась. Конкретная реализация паттерна не является паттерном.

    Ничего не развалилось. Печально, если вы не улавливаете аналогию и не в состоянии ответить на последующие вопросы. Я сделал всё, что мог.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Я поправил
    «Шарик — это собака (вид животных)»
    на
    «Шарик — это собака (животное)»

    Чтобы было очевиднее, что
    «Шарик — это животное»
    «TypeOf⟨T⟩ — это паттерн»

    Основной акцент в публикации делаю на TypeOf⟨T⟩, потому что дошёл до чёткого понимания этого паттерна лишь году на седьмом активного пользования дженериками. На мой взгляд, такое решение далеко не очевиденое, хотя довольно простое в своей основе.

    Мемоизация или не мемоизация происходит в RipeType я не знаю, но кэширование точно есть и на новизну вовсе не претендую, основная цель была в проведении сравнительных бенчмарков.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Уже объяснял, но… TypeOf⟨T⟩ — конкретная реализация паттерна кэширования (мемоизации) через статический дженерик класс (назовём паттерн для дальнейшего примера Static Generic Memorization [SGM]), RipeType — через словарь (Dictionary Memorization [DM]).

    Ровно так же, как StringBuilder — конкретная реализация паттерна Builder.

    Рассмотрим простую аналогию. Названия видов животных (собака, кошка, бегемот, слон, дельфин...) соответствуют видам паттернам (SGM, DM, фабрика, билдер...). Конкретные реализации, например, Шарик и Мурка соответствуют конкретным реализациям TypeOf⟨T⟩ и RipeType.

    Вместе с тем Шарик, являясь конкретной реализацией, не перестаёт быть собакой, как TypeOf⟨T⟩ не перестаёт быть реализацией паттена SGM.

    Утверждения
    «Шарик — это собака (животное)» являются истинными
    «TypeOf⟨T⟩ — это SGM (паттерн)» тоже истинны.

    Понятнее теперь?
  • typeof(T) vs. TypeOf⟨T⟩
    0
    можно его и под что-то еще адаптировать.
    Именно. TypeOf, RipeType — конкретные случаи.
  • typeof(T) vs. TypeOf⟨T⟩
    0

    В первую очередь код ориентирован на десктопные и мобильные приложения.

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Содержит, потому что я художник и так вижу.


    А если серьёзно, каждый может по-своему откорректировать реализацию для продакшена, если потребуется, в публикации я только делюсь идеей (паттерном), а не идеальной имплементацией на все случаи жизни.

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Да. А зачем вам описания?


    1. Clone All
    2. Find All по TypeOf

    Конечно, зесь вам самим решать, тратить на это время или нет.

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Как бы и разворачивает, но


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

    https://msdn.microsoft.com/ru-ru/library/dd335949.aspx

  • typeof(T) vs. TypeOf⟨T⟩
    0

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

  • typeof(T) vs. TypeOf⟨T⟩
    –1

    Из рубрики "Вредные советы" любителям антипаттернов


    Использовать с осторожностью! Автор ответствености не несёт! :)


    Global Lock
    using System;
    
    namespace Ace.Sugar
    {
        public static class Lock<TResult>
        {
            public static readonly object GlobalSyncContext = new object();
        }
    
        public static class Lock
        {
            public static readonly object GlobalSyncContext = new object();
    
            public static void Invoke(Action action)
            {
                lock (GlobalSyncContext) action();
            }
    
            public static void Invoke<TSyncContext>(TSyncContext customSyncContext, Action<TSyncContext> action)
            {
                lock (customSyncContext) action(customSyncContext);
            }
    
            public static TResult Invoke<TResult>(Func<TResult> func)
            {
                lock (Lock<TResult>.GlobalSyncContext) return func();
            }
    
            public static TResult Invoke<TSyncContext, TResult>(TSyncContext customSyncContext, Func<TSyncContext, TResult> func)
            {
                lock (customSyncContext) return func(customSyncContext);
            }
        }
    }

    Минусы:


    • повышенная вероятность взаимной блокировки при использовании GlobalSyncContext
    • дополнительное выделение памяти при использовании лямбда выражений
    • плохая производительность в многопоточной среде

    Плюсы:


    • не обязательно явно вводить новую переменную, позволяет ограничиваться однострочным кодом и использовать любимые лямбдочки везде и всюду, избегая ненавистных скобок { } в методах
    • не годится для продакшена, но для приложений на коленке сойдёт

    Lock.Invoke(() => DoSomething());
    Lock.Invoke(customSyncContext, c => DoSomething());
  • typeof(T) vs. TypeOf⟨T⟩
    0

    Да, везде заменил, потому что медленнее работать не будет в моих случаях, плюс общность появляется, а не где-то typeof, а где-то TypeOf.


    А плата в виде слегка повышенное потребления памяти вполне для меня допустима.

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Варианты оптимизаций я рассматриваю для каждого случая индивидуально. Касательно typeof у меня был ряд сценариев, где его избежать нельзя. Судя по результатам бенчмарков в терминах скорости выполнения, использование TypeOf в большинстве случаев даёт ощутимый выигрыш.


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

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Иногда по определённым вопросам спор с вами напоминает мне парадокс Кэррола.


    Перед вами очевидные суждения:
    А. чем меньше времени занимает событие, тем оно быстрее происходит
    Б. определённые вызовы сравниваемых методов занимают меньше времени, чем другие


    В. значит, эти вызовы быстрее


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

  • typeof(T) vs. TypeOf⟨T⟩
    0

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


    (: Шутка!

  • typeof(T) vs. TypeOf⟨T⟩
    0

    Да, текущая имплементация не лишена недостатков, но поскольку бенчмарки уже проведены на ней, в тексте публикации оставил её как есть, но добавил соответствующее примечание.


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

  • typeof(T) vs. TypeOf⟨T⟩
    –1
    Короче, если в TypeOf<> на core избавиться от статического конструктора

    Для меня это было сюрпризом, но инициализация статических полей выполняется вне/без статического конструктора.


    Добавление явного статического конструктора в текущей имплементации TypeOf влияет на скорость доступа к статическим рид-онли полям, причём, есть зависимость от типа дженерик параметра — ссылочный он или нет. На CLR замедляет TypeOf⟨int⟩ и TypeOf⟨string⟩, но на Core ускоряет TypeOf⟨int⟩, оставляя производительность TypeOf⟨string⟩ на прежнем уровне.


    Разница обусловлена кодом, генерируемым JIT-компилятором. В медленнных случаях при доступе к статическому рид-онли полю вставляется дополнительная инструкция для проверки того факта, проинициализировано оно уже или нет.


    Вообще это связано с следующими вопросами:


    • Что выполняется раньше, инициализация полей либо статический конструктор?
    • Как гарантировать потокобезопасные инициализацию полей и вызов статического конструктора?

    Сейчас в различных средах исполнения имеются свои тонкости в реализации этих механизмов.


    Вашу оптимизацию можно использовать, если, к примеру, гарантировано вызывать TypeOf⟨A⟩.Init(), TypeOf⟨B⟩.Init()… при старте приложения, пока не создались другие потоки, использующие кэшированные данные. Поскольку вызовы одиночные большого проседания в скорости запуска приложения это не вызовет (можно также такие инициализации вынести в отдельный поток). К сожалению, это вносит неудобства в использование, но зато даёт дополнительный выигрыш в производительности для критичных случаев.

  • typeof(T) vs. TypeOf⟨T⟩
    0
    Что касается typeof, то в своих вольных проектах я везде заменил его на TypeOf, потому что для меня это самое оптимальное и универсальное решение для улучшения производительности.

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

    Теперь осталось выяснить, не было ли лучшего способа решить вашу проблему.
    К сожалению, способа получения информацию о типе минуя typeof или GetType я не знаю, но знаю, как её закэшировать для наиболее быстрого доступа в дальнейшем.

    Мы же уже выяснили, что вы не делали анализа своих находок?
    По моим личным критерия анализа, решения мне подходят. Вы свои критерии знаете куда лучше, поэтому применимость находок для себя сможете определить сами.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Как вам объяснить… Где я написал, что так нужно делать везде и всегда? Если вас устраивает вариант со словарём, то, пожалуйста, пользуйтесь!

    В своей практике я дошёл до того момента, когда мне потребоволась кэшировать информацию о типах, чтобы достичь лушей производительности, и TypeOf с RipeType мне в этом помогли.

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

    У меня нет цели доказывать кому-то что-то или навязывать. Нравится решение — бери и используй, не нравится — пробуй другое.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Так в чём проблема сделать
    EqualityComparerProvider.GetDefault<T>()

    со словарём внутри, который предоставляет инстенсы EqualityComparer⟨T⟩? Почему выбрано иное решение?
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Как я написал выше, оно хорошее в определённых дженерик случаях, где требуется высокая производительность.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Если бы совсем не в кэшировании и производительности, то можно было бы просто взять и на основе словаря, как у вас, сделать
    EqualityComparer.GetDefault<T>()
    .
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Я вот вообще-то думал, что мы задачу про мемоизацию функции решаем.

    Так и я же говорю, что в определённых дженерик случаях она прекрасно решается с высокой производительностью стататическим дженерик классом с рид-онли полем.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Можете сделать RipeType дженериком (хотя это избыточно для конкретной задачи), а TypeOf⟨T⟩ переименовать в RipeType⟨T⟩ — получится та же картина, что и с EqualityComparer⟨T⟩.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    В этой дискуссии, я писал про более общий паттерн кэширования при помощи статического дженерик класса, где частными случаями являются TypeOf⟨T⟩ и EqualityComparer⟨T⟩. Насколько понимаю, вы имели ввиду под «ужасным» кодом именно этот общий случай, который я схематически обозначил, и свою критику вы не детализировали.

    Мне очень жаль, что вы не видете аналогии и не можете проследить общий паттерн.
  • typeof(T) vs. TypeOf⟨T⟩
    –1
    Рефлексия — частный случай более общего сценария с оптимиацией.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Так а что ужасного в TypeOf⟨T⟩.GetSomething()?

    Не нравится TypeOf⟨T⟩.GetSomething(), используйте эквивалентную форму TypeOf⟨T⟩.Ripe.GetSomething(), что аналогично EqualityComparer⟨T⟩.Default.Equals(a, b)
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Самое дешёвое и универсальное решение (если только у вас не микроконтроллер с минимумом памяти) в случае использования множественных рефлексивных вызовов на основе typeof в различных частях приложения — это замена этой конструкции на статическую версию TypeOf (может быть, лишь за редким исключением на определённых CLR при получении полного типа).

    И насчёт «ужасного кода» вот яркий пример
    EqualityComparer<T>.Default.Equals(a, b);
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Вот вы собственноручно сравните скорость доступа к закэшированным данным в словаре (даже минимально заполненом, 1- 5 записей) и в случае статического дженерик класса с рид-онли переменной, а потом делайте вывод, занимаюсь я ерундой или ещё чем.

    И заодно подумайте, почему
    EqualityComparer<T>.Default.Equals(a, b);

    реализован таким «ужасным» паттерном, а не хотя бы
    EqualityComparer.GetDefault<T>().Equals(a, b);
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Вот только замеров не видно и сравнения со статической версией.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    private static readonly bool _value = ValueGetter();
    
    static void AnyMethod<T>()
    {
        ... = _value;
    }

    Возможно, я двусмысленно уточнил, но _value должно быть не общим значеним для любых T, а для каждого T конкретным. Под «достаточно одного T» имелось в виду, что у метода один дженерик параметр, а различных значений T пусть будет от 10 до 100.

    Количество обращений более 100. Стоимость значения, как у typeof(T).Name/Assembly/IsValueType.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Чтобы уж точно не было разночтений по стоимости создания, можете просто взять за эталон работу с типами из публикации typeof(T).Name/Assembly/IsValueType.

    Нужно сделать быстрее в два раза при множественных вызовах.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Зачем? Это единственно верная формулировка задачи?
    В статье и примерах кода, которые вы так дерзко критикуете, решается по сути именно такая задача — закэшировать данные, зависящие от дженерик параметра, для последующего максимально быстрого доступа. Вы утверждаете, что код ужасен, тогда предложите правильное решение…

    Критерии:
    — минимальное время выполнения при многократных вызовах
    — серьёзных ограничений по памяти нет, потребление в переделах разумного
    — чтение многопоточное
    — достаточно одного T
    — кэшируемое значение любое (для простоты bool, string, object)
    — стоимость создания определяется так: если кэшированный доступ даёт выигрыш по производительности в 2 и более раза в сравнении с созданием, то задача решена
    — желательно ещё, чтобы это было справедливо для любой CLR (.NET Framework, .NET Core, Mono).
  • typeof(T) vs. TypeOf⟨T⟩
    0
    … а это удобно? Никогда бы не подумал. И код, который вы приводите, традиционно плох. Даже нет, не плох — ужасен.
    Да? Тогда задачка для вас: закэшируйте значение в статическом дженерик методе AnyPerformanceCriticalMethod⟨T⟩(), зависящее от параметра T, это же просто, верно? Мне очень интересно увидеть ваше более оптимальное по производительности решение, потому что лучшего я не знаю, а хуже да, могу предложить.

    В таком случае заодно можно считать, что этим статьям не место на хабре, потому что мне хочется думать, что аудитория здесь не состоит из «таких же тугодумов».
    Вам, может, и хочется так думать, но своих читателей на Хабре публикации находят.
  • typeof(T) vs. TypeOf⟨T⟩
    –1
    Для проведения дополнительных тестов открыты все исходные коды. Можно модифицировать их по своему усмотрению и проверять различные интересующие сценарии.

    Так что любой товарищ, участвующий в споре или наблюдающий за ним со стороны, может их провести.
  • typeof(T) vs. TypeOf⟨T⟩
    –1
    Здорово, а я вот лет 7 пользовался дженериками и только на седьмом году чётко понял, что кэшировать переменную в дженерик-методе удобно через статический дженерик класс.

    class Cache<T>
    {
        public object Instance { get; }
    }
    
    static void AnyMethod<T>()
    {
        var anyInstance = Cache<T>.Value ?? 
        Cache<T>.Value = ReadOrCreate<T>();
    }

    Поэтому можете считать, что свои посредственные статьи пишу для таких же тугодумов, как и я сам, если вам так проще.

    Возможно, вы уже ушли далеко вперёд в своём профессиональном развитии и для вас все эти замеры выглядят бессмысленно, но со скромных высот моих познаний смысл в них всё же есть.
  • typeof(T) vs. TypeOf⟨T⟩
    –2
    Извините, конечно, но в дженерик случае через статических класс — это не настолько очевидно, как в обычном. Вы сами-то применяли осознанно такое решение раньше? А если применяли, то на каком году программирования дошли?

    … и о чем же?
    Оставляю на ваш суд.

    Про чайник теперь услышал.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Но у меня не получилось воспроизвести ситуацию с недоинициализацией…

    using System;
    using System.Threading;
    using static System.Console;
    
    namespace Ace.Base.Console
    {
    	class MyClass
    	{
    		public static MyClass Instance;
    
    		private MyClass() => Thread.Sleep(5000);
    
    		public static void AsyncInit() => new Thread(() =>
    		{
    			WriteLine("Started");
    			Instance = new MyClass();
    		}).Start();
    	}
    
    	static class Program
    	{
    
    		static void Main(string[] args)
    		{
    			try
    			{
    				MyClass.AsyncInit();
    				Thread.Sleep(1000);
    				WriteLine("Ready");
    				WriteLine(MyClass.Instance?.ToString() ?? "<null>");
    			}
    			catch (Exception e)
    			{
    				WriteLine(e);
    				ReadKey(true);
    			}
    		}
    	}
    }
  • typeof(T) vs. TypeOf⟨T⟩
    –1
    Серьёзно. Это просто, но не очевидно.

    Замеры производительности на различных бенчмарках о многом говорят. И если вы считаете, что это рандомные значения, не несущие за собой смысла, то, пожалуйста, факты в студию, разрушьте мою иллюзию и раскроте глаза тем, кто в неё тоже начал верить.
  • typeof(T) vs. TypeOf⟨T⟩
    0
    Спасибо, интересный пример, он очень напоминает передачу ссылки вовне из конструктора.
    class Tester
    {
      BoxedInt2 _box = null;
      public void Set() {
        _box = new BoxedInt2();
      }
      public void Print() {
        var b = _box;
        if (b != null) b.PrintValue();
      }
    }

    По идее, можно исправить так (если компилятор не соптимизирует)
    class Tester
    {
      BoxedInt2 _box = null;
      public void Set() {
        var tmp = new BoxedInt2();
        _box = tmp;
      }
      public void Print() {
        var b = _box;
        if (b != null) b.PrintValue();
      }
    }

    Поскольку вызов конструктора — блокирующая операция.