【问题标题】:Alter property setter logic programmatically以编程方式更改属性设置器逻辑
【发布时间】:2023-04-08 12:40:01
【问题描述】:

我需要添加属性设置器的逻辑。

例如,我有一个名为“CurrentTab”的属性:

private WorkspaceViewModel _currentTab;
public WorkspaceViewModel CurrentTab
{
    get
    {
          return _currentTab;
    }
    set
    {
          _currentTab = value;
          OnPropertyChanged("CurrentTab");
    }
}

这一切都很好并且有效,但我希望能够像这样定义它:

public WorkspaceViewModel CurrentTab { get; set; }

这样系统在设置器运行后自动为属性名称执行 OnPropertyChanged() 函数,而无需我添加任何特定代码。 如何识别哪些属性需要遵循这个逻辑是没有问题的,我只需要找到一种方法来真正做到这一点。

我想让它变得更简单,因为我将拥有很多这样的属性,并且我想保持它干净。

有办法吗? 非常感谢任何帮助!

【问题讨论】:

    标签: c# .net reflection properties


    【解决方案1】:

    看看:FodyINotifyPropertyChange 有一个插件:github

    它在构建解决方案时操作 IL 代码。

    你只需要给视图模型添加属性:

    [ImplementPropertyChanged]
    public class Person 
    {        
        public string GivenNames { get; set; }
        public string FamilyName { get; set; }
    
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", GivenNames, FamilyName);
            }
        }
    }
    

    当代码被编译时:

    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        string givenNames;
        public string GivenNames
        {
            get { return givenNames; }
            set
            {
                if (value != givenNames)
                {
                    givenNames = value;
                    OnPropertyChanged("GivenNames");
                    OnPropertyChanged("FullName");
                }
            }
        }
    
        string familyName;
        public string FamilyName
        {
            get { return familyName; }
            set 
            {
                if (value != familyName)
                {
                    familyName = value;
                    OnPropertyChanged("FamilyName");
                    OnPropertyChanged("FullName");
                }
            }
        }
    
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", GivenNames, FamilyName);
            }
        }
    
        public virtual void OnPropertyChanged(string propertyName)
        {
            var propertyChanged = PropertyChanged;
            if (propertyChanged != null)
            {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      这可以使用PostSharp 来实现,这是一种Aspect Oriented Programming 方法:

      在计算中,面向方面的编程 (AOP) 是一种编程 旨在通过允许分离来增加模块化的范式 横切关注点。 AOP 是面向方面的基础 软件开发。

      您可以使用名为 InstanceLevelAspect 的 Aspect 来实现这一点:

      /// <summary> 
      /// Aspect that, when apply on a class, fully implements the interface  
      /// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to 
      /// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>. 
      /// </summary> 
      [Serializable] 
      [IntroduceInterface(typeof(INotifyPropertyChanged),  
                           OverrideAction = InterfaceOverrideAction.Ignore)] 
      [MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Property,  
                                Inheritance = MulticastInheritance.Strict)] 
      public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,  
                                                           INotifyPropertyChanged 
      { 
          /// <summary> 
          /// Field bound at runtime to a delegate of the method OnPropertyChanged
          /// </summary> 
          [ImportMember("OnPropertyChanged", IsRequired = false)]
          public Action<string> OnPropertyChangedMethod; 
      
          /// <summary> 
          /// Method introduced in the target type (unless it is already present); 
          /// raises the <see cref="PropertyChanged"/> event. 
          /// </summary> 
          /// <param name="propertyName">Name of the property.</param> 
          [IntroduceMember(Visibility = Visibility.Family, IsVirtual = true,  
                            OverrideAction = MemberOverrideAction.Ignore)] 
          public void OnPropertyChanged(string propertyName) 
          { 
              if (this.PropertyChanged != null) 
              { 
                  this.PropertyChanged(this.Instance,  
                                        new PropertyChangedEventArgs(propertyName)); 
              } 
          } 
      
          /// <summary> 
          /// Event introduced in the target type (unless it is already present); 
          /// raised whenever a property has changed. 
          /// </summary> 
          [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)] 
          public event PropertyChangedEventHandler PropertyChanged; 
      
          /// <summary> 
          /// Method intercepting any call to a property setter. 
          /// </summary> 
          /// <param name="args">Aspect arguments.</param> 
          [OnLocationSetValueAdvice,  
           MulticastPointcut( Targets = MulticastTargets.Property,  
               Attributes = MulticastAttributes.Instance)] 
          public void OnPropertySet(LocationInterceptionArgs args) 
          { 
              // Don't go further if the new value is equal to the old one. 
              // (Possibly use object.Equals here). 
              if (args.Value == args.GetCurrentValue()) 
              {
                 return; 
              }
      
              // Actually sets the value. 
              args.ProceedSetValue(); 
      
              // Invoke method OnPropertyChanged (our, the base one, or the overridden one). 
              this.OnPropertyChangedMethod.Invoke(args.Location.Name); 
          } 
      } 
      

      然后,像这样在您的财产上使用它:

      [NotifyPropertyChanged]
      public WorkspaceViewModel CurrentTab { get; set; }
      

      如果您希望所有属性都实现NotifyPropertyChanged,则此属性也可以应用于类级别。有关示例的更多信息,请参阅here

      【讨论】:

      • PostSharp 看起来很有希望,但我最终使用了 pwas 的 Fody 答案。主要是因为 PostSharp 不是免费的。感谢您的意见!
      猜你喜欢
      • 2012-08-20
      • 1970-01-01
      • 2017-02-27
      • 2017-08-05
      • 2013-01-18
      • 2015-04-09
      • 2021-09-03
      • 2016-05-22
      • 1970-01-01
      相关资源
      最近更新 更多