代码之家  ›  专栏  ›  技术社区  ›  rak007

使用postsharp在运行时更改属性类变量

  •  1
  • rak007  · 技术社区  · 6 年前

    我知道过去也有人问过类似的问题,关于围绕这个话题的类似问题,但没有一个回答了我对现代和工作C的担忧。

    在我的例子中,我试图为类变量实现一个“懒惰缓存”,因为我们使用的API允许我们同时请求特定的变量,因此我们将它们分组为小字符集以方便(并减少对API的请求数)。

    我正在使用Postshap来实现这一点 LocationInterceptionAspect 并重载每个缓存属性的getter。我将我的属性添加到变量上方,以告诉它们是哪个字符集。在我们的程序中使用的第一个变量应该以相同的字符集为其他变量加载值,并告知它们已经被加载。

    例如,假设我有4个变量 a b c d 相同字符集的 "TEST_CHARSET" .如果我愿意的话 Console.WriteLine(myObject.a) 这应该调用API来获取 “测试字符集” 字符集并填充其他变量值。我一打电话 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;
                }
            }
        }
    

    主.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);
        }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   AlexD    6 年前

    自定义属性,例如 CatalogueLazyLoad 基本上是在构建时与属性关联的元数据。不能在运行时修改其字段的值。

    还有在运行时为每个属性创建的方面的实例(它也是 Lazyload目录 )。但是这些不能通过反射API和类似 propertyInfo.GetCustomAttribute .

    您需要的是在 Lazyload目录 类。对于这种用例,在目标类中引入和导入自定义属性会很好地工作。我建议你介绍一个房产 LoadedCharsets 进入目标类。此属性将保留已加载的字符集集合,所有方面实例都将访问同一个集合实例。

    下面的示例显示如何在 Lazyload目录 类。它不处理多线程,因此如果需要,您可能需要添加多线程。

    [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>());
        }
    }