c# - 使用 Postsharp 在运行时更改属性类变量
问题描述
我知道过去有人就围绕这个主题的类似问题提出了类似的问题,但没有一个回答我对现代和工作 C# 的担忧。
就我而言,我正在尝试为我的类变量实现“延迟缓存”,因为我们使用的 API 使我们能够同时请求特定变量,因此我们将它们分组为小字符集以方便(并降低数量对 API 的请求)。
我正在使用 PostSharp 来实现这样的事情,使用LocationInterceptionAspect
并重载每个缓存属性的 getter。我将我的属性添加到我的变量上方,以告知它们在哪个字符集中。在我们的程序中使用的第一个变量应该加载相同字符集中其他变量的值,并告诉它们已加载。
例如,假设我有 4 个a b c d
相同 charset 的变量"TEST_CHARSET"
。如果我这样做Console.WriteLine(myObject.a)
应该调用 API 来获取"TEST_CHARSET"
字符集并填写其他变量值。一旦我调用Console.WriteLine(myObject.b)
,就不应调用 API,因为该值已经从上一次调用中收集。
这是一个 MVE:
懒加载.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;
}
}
}
主文件
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);
}
解决方案
诸如此类的自定义属性CatalogueLazyLoad
基本上是在构建时与您的属性相关联的元数据。您不能在运行时修改其字段的值。
还有一个在运行时为每个属性创建的切面实例(它也是 的一个实例CatalogueLazyLoad
)。但是那些不能通过反射 API 和propertyInfo.GetCustomAttribute
.
您需要一种在CatalogueLazyLoad
类的许多实例之间共享一些数据的方法。对于此类用例,将自定义属性引入和导入目标类效果很好。我建议你LoadedCharsets
在目标类中引入一个属性。此属性将保留已加载的字符集的集合,并且所有方面实例都将访问相同的集合实例。
下面的示例显示了如何在您的CatalogueLazyLoad
类中实现这一点。它不处理多线程,因此您可能需要在需要时添加它。
[PSerializable]
[MulticastAttributeUsage(PersistMetaData = true, AllowExternalAssemblies = false)]
[LinesOfCodeAvoided(50)]
// We need to implement IInstanceScopedAspect to introduce and import members
public sealed class CatalogueLazyLoad : LocationInterceptionAspect, IInstanceScopedAspect
{
public string Name { get; set; }
public string Charset { get; set; }
public CacheType Cache { get; set; }
// Introduce a new property into the target class (only once)
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public HashSet<string> LoadedCharsets { get; set; }
// Import the introduced property (it may be introduced by this aspect or another aspect on another property)
[ImportMember("LoadedCharsets", IsRequired = true, Order = ImportMemberOrder.AfterIntroductions)]
public Property<HashSet<string>> LoadedCharsetsProperty;
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.LoadedCharsetsProperty.Get().Add(this.Charset);
}
args.ProceedGetValue();
}
}
public override sealed void OnGetValue(LocationInterceptionArgs args)
{
base.OnGetValue(args);
switch (Cache)
{
case CacheType.CACHED:
bool loaded = this.LoadedCharsetsProperty.Get().Contains(this.Charset);
if (!loaded)
{
GetValue(args, true);
}
break;
case CacheType.FORCE_NO_CACHE:
GetValue(args);
break;
default:
break;
}
}
public object CreateInstance(AdviceArgs adviceArgs)
{
return this.MemberwiseClone();
}
public void RuntimeInitializeInstance()
{
this.LoadedCharsetsProperty.Set(new HashSet<string>());
}
}
推荐阅读
- android - 输入的数组索引更改 Volley 库 POST 参数中的位置
- javascript - 使用 mongodb 驱动程序通过 mongoose 连接更改流
- google-cloud-platform - 在谷歌云中使用云代理连接VM和Paas实例有多安全
- airflow - 如何将 git 的凭据传递给 bitnami 气流头盔
- curl - 并行运行 curl 中测量的时间不匹配
- android - Android - 如何从电子邮件选择器列表中排除 PayPal?
- django - 如何在没有额外请求的情况下在 Django REST Framework 中进行动态调度?
- python - Pycharm IDE 没有在我的 Windows 10 上打开 ..非常需要一个建议
- windows - 带有递归 for 循环的批处理文件以移动文件
- javascript - 网络断开时,firebase 更新不会超时或出错