Search
Write a publication
Pull to refresh
57
0

Пользователь

Send message
Как я написал выше, оно хорошее в определённых дженерик случаях, где требуется высокая производительность.
Если бы совсем не в кэшировании и производительности, то можно было бы просто взять и на основе словаря, как у вас, сделать
EqualityComparer.GetDefault<T>()
.
Я вот вообще-то думал, что мы задачу про мемоизацию функции решаем.

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

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

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

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

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

реализован таким «ужасным» паттерном, а не хотя бы
EqualityComparer.GetDefault<T>().Equals(a, b);
Вот только замеров не видно и сравнения со статической версией.
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).Name/Assembly/IsValueType.

Нужно сделать быстрее в два раза при множественных вызовах.
Зачем? Это единственно верная формулировка задачи?
В статье и примерах кода, которые вы так дерзко критикуете, решается по сути именно такая задача — закэшировать данные, зависящие от дженерик параметра, для последующего максимально быстрого доступа. Вы утверждаете, что код ужасен, тогда предложите правильное решение…

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

В таком случае заодно можно считать, что этим статьям не место на хабре, потому что мне хочется думать, что аудитория здесь не состоит из «таких же тугодумов».
Вам, может, и хочется так думать, но своих читателей на Хабре публикации находят.
Для проведения дополнительных тестов открыты все исходные коды. Можно модифицировать их по своему усмотрению и проверять различные интересующие сценарии.

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

class Cache<T>
{
    public object Instance { get; }
}

static void AnyMethod<T>()
{
    var anyInstance = Cache<T>.Value ?? 
    Cache<T>.Value = ReadOrCreate<T>();
}

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

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

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

Про чайник теперь услышал.
Но у меня не получилось воспроизвести ситуацию с недоинициализацией…

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);
			}
		}
	}
}
Серьёзно. Это просто, но не очевидно.

Замеры производительности на различных бенчмарках о многом говорят. И если вы считаете, что это рандомные значения, не несущие за собой смысла, то, пожалуйста, факты в студию, разрушьте мою иллюзию и раскроте глаза тем, кто в неё тоже начал верить.
Спасибо, интересный пример, он очень напоминает передачу ссылки вовне из конструктора.
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();
  }
}

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

Information

Rating
Does not participate
Registered
Activity