使用 CodeContracts 强制执行 INotifyPropertyChanged 的​​正确实现 - “需要未经证实”

发布于 2024-08-08 10:25:17 字数 985 浏览 6 评论 0原文

我正在寻找一种简单的方法来强制执行 INotifyPropertyChanged 的​​正确实现,即当引发 PropertyChanged 时,它必须引用实际定义的属性。我尝试使用 Microsoft 的新 CodeContract 工具执行此操作,但我不断收到警告“CodeContracts:需要未经验证”。这是我的代码...

public sealed class MyClass : INotifyPropertyChanged
{
    private int myProperty;
    public int MyProperty
    {
        get
        {
            return myProperty;
        }
        set
        {
            if (myProperty == value)
            {
                return;
            }

            myProperty = value;
            OnPropertyChanged("MyProperty");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        Contract.Requires(GetType().GetProperties().Any(x => x.Name == propertyName));

        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

有什么办法让它工作吗?

I'm looking for an easy way to enforce the correct implementation of INotifyPropertyChanged i.e. when PropertyChanged is raised it must reference a property that is actually defined. I tried doing this with the new CodeContract tools from Microsoft, but I keep getting the warning "CodeContracts: requires unproven". Here is my code...

public sealed class MyClass : INotifyPropertyChanged
{
    private int myProperty;
    public int MyProperty
    {
        get
        {
            return myProperty;
        }
        set
        {
            if (myProperty == value)
            {
                return;
            }

            myProperty = value;
            OnPropertyChanged("MyProperty");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        Contract.Requires(GetType().GetProperties().Any(x => x.Name == propertyName));

        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Is there anyway to get this to work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

差↓一点笑了 2024-08-15 10:25:17

好的,首先,为此目的,我个人使用 MVVM 基础。这是一个仅调试构建的运行时检查,与您的几乎相同。

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
    // Verify that the property name matches a real,  
    // public, instance property on this object.
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
    {
        string msg = "Invalid property name: " + propertyName;

        if (this.ThrowOnInvalidPropertyName)
            throw new Exception(msg);
        else
            Debug.Fail(msg);
    }
}

这可能是最简单的方法,但它有一定的缺点:您需要能够从某个基类继承,它只在运行时有效(尽管这在我的 wpf 经验中总是足够的),它看起来确实像一个“补丁”缺少静态检查。

对于这种情况,您有多种方法启用静态分析/静态工具:

  1. 就像Marc所说,使用 lambda 表示法并在运行时提取字符串
  2. 编写自定义 FxCop 规则
  3. 使用 AOP 工具通过一些元标记来后处理代码

至于CodeContracts,我认为它还不够成熟,无法处理静态分析中的此类检查。想象一下,它必须解析您的 lambda,了解如何因错误的 propertyName 而失败,找到对此方法的所有调用,找出所有可能的输入等。这只是一个错误的工具一种检查。

Ok, first of all, for this purpose I personally use ObservableObject implementation from the MVVM foundation. It is a DEBUG-build only runtime check almost identical to yours.

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
    // Verify that the property name matches a real,  
    // public, instance property on this object.
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
    {
        string msg = "Invalid property name: " + propertyName;

        if (this.ThrowOnInvalidPropertyName)
            throw new Exception(msg);
        else
            Debug.Fail(msg);
    }
}

It's probably the easiest way, but it has certain disadvantages: you need to be able to inherit from some base class, it only works in runtime (though this was always enough in my wpf-experience), it surely looks like a "patch" for a missing static check.

You have several ways to enable static analysis / static tools for this case:

  1. Like Marc says, use lambda notation and extract string in run-time.
  2. Write a custom FxCop rule.
  3. Use an AOP tool to post-process code with some meta-markup.

As for the CodeContracts, I believe it is not yet mature enough to handle this kind of checks in static analysis. Imagine, it has to parse your lambda, understand how it can be failed by a wrong propertyName, find all calls to this method, figure out all possible inputs, etc. It is just a wrong instrument for that kind of check.

郁金香雨 2024-08-15 10:25:17

我想你的意思是静态分析工具? (我希望运行时检查至少能够工作 - 并且您可能可以将其保留在调试版本中)。我怀疑静态分析是否能够看穿这一点 - GetType().GetProperties() 太复杂了,等等

。我对此表示怀疑... lambda(Expression)是一种选择,但它们比仅传递字符串要慢得多。

I assume you mean with the static analysis tools? (I would expect the runtime check to work, at least - and you could presumably leave it in debug builds). I doubt that this is something that static analysis is going to be able to see through - GetType().GetProperties() is simply too complex, etc.

In short; I doubt it... lambdas (Expression) are an option, but they are much slower than passing just a string.

冷情妓 2024-08-15 10:25:17

我过去这样做的方法是使用我们的好朋友 Lambda。通过使用表达式,我们可以将属性本身传递给 OnPropertyChanges 的实现,并使用表达式树来提取属性。这使您可以在编译时检查为其引发 PropertyChanged 事件的成员。

当然,表达式的使用完全取决于您需要什么类型的性能。

请参阅下面的代码片段:

using System;
using System.Linq;
using System.ComponentModel;
using System.Linq.Expressions;

namespace OnNotifyUsingLambda
{
    public class MainClass : INotifyPropertyChanged
    {
         public static void Main (string[] args) { new MainClass().Run();}
         public void Run()
         {
              this.PropertyChanged += (sender, e) => Console.WriteLine(e.PropertyName);
              MyProperty = "Hello";
         }

         private string myProperty;
         public string MyProperty  
         {
             get
             {
                 return myProperty;
             }
             set
             {
                 myProperty = value;
                 // call our OnPropertyChanged with our lamba expression, passing ourselves.
                 // voila compile time checking that we haven't messed up!
                 OnPropertyChanged(x => x.MyProperty); 
              }
         }  

         /// <summary>
         /// Fires the PropertyChanged for a property on our class.
         /// </summary>
         /// <param name="property">
         /// A <see cref="Expression<Func<MainClass, System.Object>>"/> that contains the 
         /// property we want to raise the event for.
         /// </param>
         private void OnPropertyChanged (Expression<Func<MainClass, object>> property)
         {
             // pull out the member expression (ie mainClass.MyProperty)
             var expr = (MemberExpression)property.Body; 

             if (PropertyChanged != null)
             {
                 // Extract everything after the period, which is our property name.
                 var propName = expr.ToString ().Split (new[] { '.' })[1];
                 PropertyChanged (this, new PropertyChangedEventArgs(propName));
             }
          }

          public event PropertyChangedEventHandler PropertyChanged;
     }
}

The way I have done this in the past is to use our good friend Lambda. By using Expressions we can pass in the properties themselves to your implementation of OnPropertyChanges, and use the Expression tree to extract the property. This gives you compile time checking of the members you are raising the PropertyChanged event for.

Of course use of Expression will depend entirely on what type of performance you need.

See code snippet below:

using System;
using System.Linq;
using System.ComponentModel;
using System.Linq.Expressions;

namespace OnNotifyUsingLambda
{
    public class MainClass : INotifyPropertyChanged
    {
         public static void Main (string[] args) { new MainClass().Run();}
         public void Run()
         {
              this.PropertyChanged += (sender, e) => Console.WriteLine(e.PropertyName);
              MyProperty = "Hello";
         }

         private string myProperty;
         public string MyProperty  
         {
             get
             {
                 return myProperty;
             }
             set
             {
                 myProperty = value;
                 // call our OnPropertyChanged with our lamba expression, passing ourselves.
                 // voila compile time checking that we haven't messed up!
                 OnPropertyChanged(x => x.MyProperty); 
              }
         }  

         /// <summary>
         /// Fires the PropertyChanged for a property on our class.
         /// </summary>
         /// <param name="property">
         /// A <see cref="Expression<Func<MainClass, System.Object>>"/> that contains the 
         /// property we want to raise the event for.
         /// </param>
         private void OnPropertyChanged (Expression<Func<MainClass, object>> property)
         {
             // pull out the member expression (ie mainClass.MyProperty)
             var expr = (MemberExpression)property.Body; 

             if (PropertyChanged != null)
             {
                 // Extract everything after the period, which is our property name.
                 var propName = expr.ToString ().Split (new[] { '.' })[1];
                 PropertyChanged (this, new PropertyChangedEventArgs(propName));
             }
          }

          public event PropertyChangedEventHandler PropertyChanged;
     }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文