You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CallerMemberName实现INotifyPropertyChanged的报错排查与解决

问题分析与解决步骤

我来帮你梳理并解决你遇到的这几个问题:

第一个异常:System.TypeLoadException的原因与修复

你最初的PropertyChangedEventArgs类里,给构造函数加了extern修饰符:

public extern PropertyChangedEventArgs(string propertyName);

extern关键字是用来声明由外部代码(比如非托管C++)实现的方法,但你并没有为这个构造函数提供任何外部实现逻辑,所以CLR在加载这个类型时,找不到构造函数的实际执行代码(也就是错误提示里的“no RVA”,相对虚拟地址),直接抛出了TypeLoadException。

修复方法:
去掉extern修饰符,同时给构造函数添加实际的赋值逻辑,就像你后来改写的那样:

public class PropertyChangedEventArgs : EventArgs {
    public PropertyChangedEventArgs(string propertyName) {
        PropertyName = propertyName;
    }
    public string PropertyName { get; }
}

第二个异常:System.NotImplementedException的原因与修复

这个异常完全是因为你注册的事件处理方法Foo_PropertyChanged里直接抛出了未实现异常:

private static void Foo_PropertyChanged(object sender, PropertyChangedEventArgs e) {
    throw new NotImplementedException();
}

当CustomerName属性变更触发PropertyChanged事件时,这个方法会被执行,自然就会抛出异常。你提到“最终代码可正常运行”,应该是你后续调整了这个方法的实现逻辑。

修复方法:
把这个方法替换成你写的有实际业务逻辑的propertyChanged方法,或者直接修改它的实现,比如:

private static void Foo_PropertyChanged(object sender, PropertyChangedEventArgs e) {
    var foo = sender as Foo;
    Console.WriteLine($"属性 {e.PropertyName} 已更新为: {foo?.CustomerName}");
    if (e.PropertyName == "CustomerName" && foo?.CustomerName == "dada") {
        Console.WriteLine("CustomerName成功变更为dada");
    }
}

完整的可运行代码

把所有问题修复后,完整的可运行代码如下:

using System;
using System.Runtime.CompilerServices;

namespace Caller_Info_Attributes {
    public interface INotifyPropertyChanged {
        event PropertyChangedEventHandler PropertyChanged;
    }

    public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);

    public class PropertyChangedEventArgs : EventArgs {
        public PropertyChangedEventArgs(string propertyName) {
            PropertyName = propertyName;
        }
        public string PropertyName { get; }
    }

    public class Foo : INotifyPropertyChanged {
        // 初始化空委托,避免事件为null时调用报错
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        void RaisePropertyChanged([CallerMemberName] string propertyName = null) {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        string customerName;
        public string CustomerName {
            get { return customerName; }
            set {
                if (value == customerName) return;
                customerName = value;
                // 编译器自动传入当前属性名"CustomerName"
                RaisePropertyChanged();
            }
        }
    }

    class Test {
        static void Main(string[] args) {
            Foo foo = new Foo();
            foo.PropertyChanged += Foo_PropertyChanged;
            foo.CustomerName = "abra";
            foo.CustomerName = "dada";
            Console.WriteLine("操作完成");
            Console.ReadLine();
        }

        private static void Foo_PropertyChanged(object sender, PropertyChangedEventArgs e) {
            var fooInstance = sender as Foo;
            Console.WriteLine($"检测到属性变更: {e.PropertyName} = {fooInstance?.CustomerName}");
            if (e.PropertyName == "CustomerName" && fooInstance?.CustomerName == "dada") {
                Console.WriteLine("CustomerName已更新为目标值dada");
            }
        }
    }
}

额外小提示

  • [CallerMemberName]特性的核心作用就是让编译器自动把调用该方法的成员名称(这里就是CustomerName属性)作为参数传入,所以你完全不需要手动写RaisePropertyChanged("CustomerName"),编译器会帮你完成这个工作。
  • 给PropertyChanged事件初始化一个空委托(= delegate { };)是WPF/VM开发里的常见最佳实践,用来避免事件未被订阅时调用PropertyChanged(...)抛出NullReferenceException。

内容的提问来源于stack exchange,提问作者streamc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:15:26