using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
//EXTERNAL CODE
//Приведен только для справки, никак нельзя менять, код обфусцирован, исходников нет
public sealed class ExternalDataProvider : IDisposable
{
public extern int LongRunningCalculation(int value, int value2);
public extern void Dispose();
}
/// <summary>
/// Объект-значение
/// <para>Базовый класс</para>
/// </summary>
public abstract class ValueObject<T> where T : ValueObject<T>
{
public static bool operator ==(ValueObject<T> value1, ValueObject<T> value2)
{
if ((object)value1 == null)
return (object)value2 == null;
return value1.Equals(value2);
}
public static bool operator !=(ValueObject<T> value1, ValueObject<T> value2)
{
return !(value1 == value2);
}
public override bool Equals(object obj)
{
var valueObject = obj as T;
if (ReferenceEquals(valueObject, null))
return false;
return CompareValues(valueObject);
}
public override int GetHashCode()
{
return GetValueHashCode();
}
/// <summary>
/// Получить HashCode
/// </summary>
/// <returns></returns>
protected abstract int GetValueHashCode();
/// <summary>
/// Сравнить значения
/// </summary>
/// <returns></returns>
protected abstract bool CompareValues(T other);
}
/// <summary>
/// Параметр для DataProvider
/// </summary>
public class Parameter : ValueObject<Parameter>
{
private Parameter()
{
FirstValue = -1;
SecondValue = -1;
}
public Parameter(int firstValue, int secondValue)
{
if (firstValue > 100 || firstValue < 0 || secondValue < 1 || secondValue > 12)
throw new ArgumentException("Is not supported values");
FirstValue = firstValue;
SecondValue = secondValue;
}
/// <summary>
/// Первый параметр
/// </summary>
public int FirstValue { get; }
/// <summary>
/// Второй параметр
/// </summary>
public int SecondValue { get; }
/// <summary>
/// Default value
/// </summary>
public static readonly Parameter Empty = new Parameter();
protected override bool CompareValues(Parameter other)
{
return FirstValue == other.FirstValue && SecondValue == other.SecondValue;
}
protected override int GetValueHashCode()
{
return (FirstValue, SecondValue).GetHashCode();
}
}
/// <summary>
/// Результат для DataProvider
/// </summary>
public class Result : ValueObject<Result>
{
private Result()
{
// Default value
Value = default;
}
public Result(int value)
{
Value = value;
}
/// <summary>
/// Результат
/// </summary>
public int Value { get; }
/// <summary>
/// Default value
/// </summary>
public static readonly Result Empty = new Result();
protected override bool CompareValues(Result other)
{
return Value == other.Value;
}
protected override int GetValueHashCode()
{
return Value.GetHashCode();
}
}
/// <summary>
/// Интерфейс DataProvider
/// </summary>
public interface IDataProvider
{
public Result GetResult(Parameter parameter);
}
/// <summary>
/// Получить данные из ExternalDataProvider без кеша
/// </summary>
public class DataProvider : IDataProvider
{
public Result GetResult(Parameter parameter)
{
using var externalDataProvider = new ExternalDataProvider();
return new Result (externalDataProvider.LongRunningCalculation(parameter.FirstValue, parameter.SecondValue));
}
}
/// <summary>
/// DataProvider с кешированием данных
/// Реализация кеша не имеет принципиального значения
/// </summary>
public class CacheDataProvider : IDataProvider
{
private readonly IDataProvider dataProvider;
private readonly ConcurrentDictionary<Parameter, Result> cache = new();
public CacheDataProvider(IDataProvider dataProvider)
{
this.dataProvider = dataProvider;
}
public Result GetResult(Parameter parameter)
{
return cache.GetOrAdd(parameter, (p => dataProvider.GetResult(p)));
}
}
зачем делать Singleton with double check locking
сказали что lock это одна из самых дешевых оперций по синхронизации.
get { lock (Loker) { // only one check and everything is fine :) if (_instance == null) { _instance = new Singleton1(); } } return _instance; }
В основном этот интерфейс используется для высвобождения неуправляемых ресурсов. Сборщик мусора автоматически освобождает память, выделенную управляемому объекту, если этот объект больше не используется. Однако невозможно предсказать, когда произойдет сборка мусора. Более того, сборщик мусора не имеет сведений о неуправляемых ресурсах, таких как дескрипторы окон, или открытых файлах и потоках.
Используйте Dispose метод этого интерфейса для явного освобождения неуправляемых ресурсов в сочетании с сборщиком мусора. Потребитель объекта может вызвать этот метод, если объект больше не нужен.
public sealed class DataProvider : IDisposable
{
// nit: Предложил бы названия firstValue, secondValue либо более осмысленные, если возможно
public extern int LongRunningCalculation(int value, int value2);
public extern void Dispose();
}
// nit: сразу бы хотелось видеть уровень доступа и sealed (если класс не планируется наследовать)
// Class2 - дать нормальное имя
// { - перенести на 2ю строку по рекомендациям code style от microsoft (если не принято иных)
class Class2 {
// Синхронизация не нужна, если убрать метод Init, а Create вызвать в статическом конструкторе
private readonly object _sync = new object();
// _ht - дать осмысленное название
// Судя по использованию, value может быть int`ом. Не зачем иметь лишний boxing и проверки на тип
// _ht статический, значит к нему могут быть обращения из разных потоков, лучше сделать его ConcurrentDictionary
// Прям сходу не могу сказать, но, возможно, использовал бы какой то другой тип Dictionary <key, key, val> (самописный или существующий), кажется, так было бы быстрее чем массив в ключе
private static Dictionary<int[], object> _ht;
// nit: хотелось бы имена со смыслом
public int GetValue(int index, int index2)
{
// Лишний метод, удалить. Create вызовем в static конструкторе
Init();
// Если ключ у нас объект, то необходимо реализовать IEqualityComparer для этого Dictionary (иначе не понятно как по нему искать)
var key = new[] {index, index2};
// Проверка на тип не нужна, Dictionary сделаем типа int
if (_ht.ContainsKey(key) & _ht[key].GetType() == typeof(int))
// приведение типов больше не нужно
return ((int)_ht[key]);
// nit: else не обязателен
else
// int не может быть null, будет ошибка, вернуть либо default, либо возвращаемое значение должно быть int?
return null;
}
// Метод удалить, вызовем Create в статическом конструкторе без lock
public void Init()
{
if (_ht == null)
lock (_sync)
Create();
}
// Нет смысла делать метод public, сделать private
public static void Create()
{
// nit: и так видно какой тип создаём, можно использовать var
// Обернуть в using
DataProvider provider = new DataProvider();
// Тут следует инициализировать значение _ht, т.к. ранее оно нигде не создаётся
// Не забыть передать реализацию IEqualityComparer в конструктор
// nit: хотелось бы видеть использование фигурных скобок (если не принят иной code style)
// nit: вместо int можно var
// i и j, похоже, несут какой то смысл, можно попробовать придумать нормальное название (иначе не понятно почему 100 и 12, их можно в константы класса)
// nit: возможно можно использовать Parallel.ForEach
for (int i = 0; i < 100; i++)
for (int j = 1; j <= 12; j++)
_ht[new [] { i, j }] = provider.LongRunningCalculation(i, j);
}
}
public interface IDataProvider : IDisposable
{
int LongRunningCalculation(int firstValue, int secondValue);
}
public sealed class DataProvider : IDataProvider
{
public extern int LongRunningCalculation(int firstValue, int secondValue);
public extern void Dispose();
}
public sealed class DataProviderService
{
public DataProviderService(IDataProvider dataProvider)
{
_dataProvider = dataProvider;
}
private static readonly ConcurrentDictionary<int[], int?> _calculatedCache = new ConcurrentDictionary<int[], int?>(new CalculatedEqualityComparer());
private readonly IDataProvider _dataProvider;
public int? GetValue(int firstValue, int secondValue)
{
var isNotSupportedValues = firstValue > 100 || firstValue < 0 || secondValue < 1 || secondValue > 12;
if (isNotSupportedValues)
{
return null;
}
var key = new[] { firstValue, secondValue };
if (!_calculatedCache.TryGetValue(key, out var result))
{
result = _dataProvider.LongRunningCalculation(firstValue, secondValue);
_calculatedCache.TryAdd(key, result);
}
return result;
}
}
public extern int LongRunningCalculation(int value, int value2);
public int GetValue(int index, int index2)
Объект – конкретная реализация абстрактного типа, обладающий характеристиками состояния, поведения, индивидуальности.
Состояние – один из возможных вариантов условий существований объекта.
Поведение – описание объекта в терминах изменения его состояния и передача сообщений (данных) в процессе воздействия.
Индивидуальность – сущность объекта, отличающееся от других объектов.
Действие – деятельность или операция, которые должны быть выполнены над объектом.