."); 7 } 8 9 public void TraceMessage(string message, 10 [CallerMemberName] string memberName 成员名称 可以使用 CallerMemberName 属性设置为来避免指定成员名称作为参数 String 到调用的方法。 通过使用这种方法,可以避免此问题 重命名重构 不更改 String 值。 不 CallerMemberName 属性,必须指定属性名称为文本。 以下图表显示返回的成员名称,当您使用 CallerMemberName 属性。
在 C# 中有一个特性 CallerMemberName 可以给方法知道调用这个方法的方法名,在 UWP 中用这个特性很多,特别是在使用 MVVM 绑定 如果在构造函数使用这个特性会发生什么? 先定义一个类 public class F1 { /// <inheritdoc /> public F1([CallerMemberName] string (); } private void Foo() { new F1(); } 在方法调用的时候,小伙伴都知道 CallerMemberName ] IL_000c: nop // [52 9 - 52 10] IL_000d: ret } 可以看到虽然调用的方法看起来是相同的,但是运行的结果是不相同的,因为 CallerMemberName 是在编译时做的 从上面的 IL 也可以看到 CallerMemberName 是在编译的时候传入的值,性能会比反射快,如果需要获得调用这个方法的方法的方法,也就是 A 调 B 然后 B 调 C 需要在
在 C# 中有一个特性 CallerMemberName 可以给方法知道调用这个方法的方法名,在 UWP 中用这个特性很多,特别是在使用 MVVM 绑定 如果在构造函数使用这个特性会发生什么? 先定义一个类 public class F1 { /// <inheritdoc /> public F1([CallerMemberName] string (); } private void Foo() { new F1(); } 在方法调用的时候,小伙伴都知道 CallerMemberName ] IL_000c: nop // [52 9 - 52 10] IL_000d: ret } 可以看到虽然调用的方法看起来是相同的,但是运行的结果是不相同的,因为 CallerMemberName 是在编译时做的 从上面的 IL 也可以看到 CallerMemberName 是在编译的时候传入的值,性能会比反射快,如果需要获得调用这个方法的方法的方法,也就是 A 调 B 然后 B 调 C 需要在
其实在很早.NET就支持自动获取调用方的信息了,在.NET6以前我们如果要获取调用方的信息可以使用这三个Caller Info Attribute: CallerMemberName:调用方名称,方法名或者属性名 callerLineNumber = null, [CallerMemberName] string? callerMemberName = null) { Console.WriteLine("Caller info:"); Console.WriteLine($@"CallerFilePath : {callerFilePath} CallerLineNumber: {callerLineNumber} CallerMemberName: {callerMemberName}"); } 这里要注意的是
public void TraceMessage(string message, 7 [System.Runtime.CompilerServices.CallerMemberName 成员名称 可以使用 CallerMemberName 特性来避免将成员名称指定为所调用的方法的 String 参数。 通过使用这种技术,可以避免“重命名重构”不更改 String 值的问题。 如果没有 CallerMemberName 特性,则必须将属性名称指定为文本。 以下图表显示在使用 CallerMemberName 特性时返回的成员名称。
string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName
public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged([CallerMemberName this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Data")); //新代码 this.RaisePropertyChanged(); 此处要解释一下,CallerMemberName
param> /// <param name="propertyName"></param> protected void SetValue(object value, [CallerMemberName GetValue<T>([CallerMemberName] string propertyName = null!) => GetValue<T>(default! propertyName"></param> /// <returns></returns> protected T GetValue<T>(T defaultValue, [CallerMemberName
如果使用 CallerMemberName 属性,对 NotifyPropertyChanged 方法不必指定属性名称作为字符串参数。 88 89 // This method is called by the Set accessor of each property. 90 // The CallerMemberName name of the caller to be substituted as an argument. 92 private void NotifyPropertyChanged([CallerMemberName
default); 通过支持解构语法中的默认文字,以下语法也可以实现相同的功能: (int x, int y) = default; 8.Caller Argument Expression 在C#5中,引入了CallerMemberName 就像CallerMemberName在INotifyPropertyChanged中的应用,对于WPF开发的童鞋就在熟悉不过了: class ViewModel : INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName
那么,我们来用CallerMemberName继续简化这个ViewModel。 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName 最后我们通过CallerMemberName特性,在方法OnPropertyChanged里来获取触发该方法的属性的名称。 然后我们就实现了,比较简洁的ViewModel。 PS:CallerMemberName的用法就好像param参数一样,只要如上所示,写进去即可。 结语 到此,消息的应用就讲完了。消息毫无疑问是MVVM的技术核心。学会消息才能更好的理解MVVM。
利用这个属性, 可以将上面提供的 SetProperty 方法进行改造, 这样的实现才是最完美的: protected void SetProperty<T>(ref T storage, T value, [CallerMemberName object.Equals(storage, value)) return; storage = value; this.OnPropertyChanged(propertyName); } 由于有了 CallerMemberName
> /// <param name="propertyName"></param> protected virtual void OnPropertyChanged([CallerMemberName PropertyChangedEventArgs(propertyName)); } protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName return true; } protected bool SetPropertyWithoutCompare<T>(ref T storage, T value, [CallerMemberName ,false - 为空 </returns> public virtual bool ValidateBlank(object value, string errMsg = "", [CallerMemberName
Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
protected void OnPropertyChanged([CallerMemberName "";
protected virtual void SetValue(string Key, string value) { }
///
利用CallerMemberName 改造。 改造完,清爽多了。 运行,完全正常。
."); } public void TraceMessage(string message, [System.Runtime.CompilerServices.CallerMemberName string traceId = null, string userId = null, [System.Runtime.CompilerServices.CallerMemberName
."); } public static void TraceMessage(string message, [CallerMemberName
StartNewActivity(this ActivitySource activitySource, ActivityKind kind = ActivityKind.Internal, [CallerMemberName this ActivitySource activitySource, ActivityKind kind = ActivityKind.Internal, [CallerMemberName
[NotifyPropertyChangedInvocator] protected void SetValue<T>(ref T field, T value, [CallerMemberName ; } [NotifyPropertyChangedInvocator] protected void NotifyPropertyChanged([CallerMemberName
/// 显示消息 /// private void ShowInfo(string info, [CallerFilePath] string filePath = "", [CallerMemberName / TBInfo.Text += $"[{DateTime.Now:HH:mm:ss.ffff}] {info}\r\n\r\n"; //} 可以看到方法新增了以 CallerFilePath、CallerMemberName 输出重定向(带调用方信息) /// /// <param name="write"> 日志方法委托(后三个参数为 CallerFilePath、CallerMemberName