Я знаю, что в прошлом задавались похожие вопросы о похожих вещах вокруг этой темы, но ни один из них не ответил на мою озабоченность современным и работающим C #.
В моем случае я пытаюсь реализовать «ленивый кеш» для моих переменных класса, потому что API, который мы используем, позволяет нам запрашивать конкретные переменные одновременно, поэтому мы сгруппируем их для удобства (и уменьшить количество запросов к API).
Я использую PostSharp для достижения этой цели, используя LocationInterceptionAspect
и перегружая метод получения каждого кэшированного свойства. Я добавляю свой атрибут над моей переменной, чтобы сказать, в какой кодировке они находятся. Первая переменная, которая будет использоваться в нашей программе, должна загружать значения для других в той же кодировке и сообщать, что они были загружены.
Например, допустим, у меня есть 4 переменные a b c d
одной и той же кодировки "TEST_CHARSET"
. Если я сделаю Console.WriteLine(myObject.a)
, это вызовет API, чтобы получить кодировку "TEST_CHARSET"
и заполнить значения других переменных. Как только я вызываю Console.WriteLine(myObject.b)
, никакие вызовы API не должны выполняться, поскольку значение уже было получено из предыдущего вызова.
Вот MVE:
LazyLoad.cs
[PSerializable]
[MulticastAttributeUsage(PersistMetaData = true, AllowExternalAssemblies = false)]
[LinesOfCodeAvoided(50)]
public sealed class CatalogueLazyLoad : LocationInterceptionAspect
{
#region PROPERTIES
public string Name { get; set; }
public string Charset { get; set; }
public CacheType Cache { get; set; }
public bool Loaded { get; set; } = false;
#endregion
public CatalogueLazyLoad(string name, string charset)
{
Name = name;
Charset = charset;
Cache = CacheType.CACHED;
}
private void GetValue(LocationInterceptionArgs args, bool propagate = false)
{
var properties = args.Instance.GetType().GetProperties();
// JSONObject is just an object with string KEY and string VALUE, you can add dummy data here using a Dictionary<string, string>
IEnumerable<JSONObject> result = API.Methods.GetCharsetData(id, Charset).Result;
if (result.Count() > 0)
{
foreach (PropertyInfo propertyInfo in properties)
{
CatalogueLazyLoad attribute = propertyInfo.GetCustomAttribute<CatalogueLazyLoad>();
if (attribute != null && attribute.Charset == Charset)
{
propertyInfo.SetValue(args.Instance, Convert.ChangeType(result.Where(x => x.Key == attribute.Name).Select(x => x.Value).FirstOrDefault(),
propertyInfo.PropertyType, CultureInfo.CurrentCulture), null);
if (propagate)
{
// THIS IS WHERE I AM STUCK, HOW TO SET true to LOADED of OTHERS ATTRIBUTES ??
propertyInfo.GetCustomAttribute<CatalogueLazyLoad>().Loaded = true;
}
}
}
args.ProceedGetValue();
}
}
public override sealed void OnGetValue(LocationInterceptionArgs args)
{
base.OnGetValue(args);
switch (Cache)
{
case CacheType.CACHED:
if (!Loaded)
{
GetValue(args, true);
Loaded = true;
}
break;
case CacheType.FORCE_NO_CACHE:
GetValue(args);
break;
default:
break;
}
}
}
Main.cs
public class Test
{
[CatalogueLazyLoad("a", "TEST_CHARSET")]
public string a { get; set; }
[CatalogueLazyLoad("b", "TEST_CHARSET")]
public string b { get; set; }
[CatalogueLazyLoad("c", "TEST_CHARSET")]
public string c { get; set; }
[CatalogueLazyLoad("d", "TEST_CHARSET")]
public string d { get; set; }
}
static void Main()
{
Test test = new Test();
Console.WriteLine(test.a);
// This should not call the API
Console.WriteLine(test.b);
}