我遇到了一个问题,我需要获取属性的名称以进行日志记录。我确信有一种方法可以在 VB.Net 中使用一些反射和 lambda 表达式的混合体来做到这一点,但到目前为止我还没有成功。
我想要做的是转换这个:
objAddress.AddressLine
对此:
"AddressLine"
我遇到了一个问题,我需要获取属性的名称以进行日志记录。我确信有一种方法可以在 VB.Net 中使用一些反射和 lambda 表达式的混合体来做到这一点,但到目前为止我还没有成功。
我想要做的是转换这个:
objAddress.AddressLine
对此:
"AddressLine"
过去,我使用我在网上找到的一种方法来执行此操作INotifyPropertyChanged
。我不记得确切的位置,但这详细说明了相同的分辨率:
http://paulstovell.com/blog/strong-property-names
C#
public class Test : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged(() => Name);
}
}
private void RaisePropertyChanged(Expression<Func<object>> property)
{
MemberExpression exp = property.Body as MemberExpression;
if (exp != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(exp.Member.Name));
}
}
}
VB
Public Class Test
Implements INotifyPropertyChanged
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(value As String)
_Name = value
RaisePropertyChanged(Function() Me.Name)
End Set
End Property
Private Sub RaisePropertyChanged(Of T)(propertyName As Expression(Of Func(Of T)))
Dim exp As MemberExpression = TryCast(propertyName.Body, MemberExpression)
If exp IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(exp.Member.Name))
End If
End Sub
End Class
这样做的主要好处是重构。如果我重命名我的属性,Lambda(以及扩展的 NotifyPropertyChanged 事件)会自动更改。
更新 (2015)
值得一提的是,Visual Studio 2015 中的新功能使这变得更加容易。下面是上面显示的相同代码,但使用了新nameof
功能(可以在此处找到有关此功能和其他新功能的详细信息)。
C#
public class Test : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged(nameof(Name));
}
}
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
VB
Public Class Test
Implements INotifyPropertyChanged
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(value As String)
_Name = value
RaisePropertyChanged(NameOf(Name))
End Set
End Property
Private Sub RaisePropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
您甚至可以nameof
在订阅者端使用,以确定该属性是否是您关心的属性:
private static void PropChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Test.Name))
{
Console.WriteLine("The Property I care about changed");
}
}
对于仍在寻找优雅解决方案的人,在最新版本的 VB 和 C# 中,您可以使用“nameof()”。
C#:
var propertyName = nameof(objAddress.AddressLine)
VB.NET:
Dim propertyName = nameof(objAddress.AddressLine)
请注意,如果您没有对象的实例,则可以只使用它的类名。
这将显示当前的方法。您可能想要替换属性名称的“get_”前缀。
Public Class People
Public Shared ReadOnly Property Address As String
Get
Return System.Reflection.MethodInfo.GetCurrentMethod().ToString()
End Get
End Property
End Class
' print it
System.Diagnostics.Debug.Print(People.Address)
来自MSDN
Imports System
Imports System.Reflection
Class MyClass1
Private myProperty1 As Integer
' Declare MyProperty.
Public Property MyProperty() As Integer
Get
Return myProperty1
End Get
Set(ByVal Value As Integer)
myProperty1 = Value
End Set
End Property
End Class 'MyClass1
Public Class MyTypeClass
Public Shared Sub Main(ByVal args() As String)
Try
' Get Type Object corresponding to MyClass.
Dim myType As Type = GetType(MyClass1)
' Get PropertyInfo object by passing property name.
Dim myPropInfo As PropertyInfo = myType.GetProperty("MyProperty")
' Display Name propety to console.
Console.WriteLine("The {0} property exists in MyClass.", myPropInfo.Name)
Catch e As NullReferenceException
Console.WriteLine("The property does not exist in MyClass.", e.Message.ToString())
End Try
End Sub 'Main
End Class 'MyTypeClass
我决定使用 the_lotus 对我最初的问题的回答,即使用常量。如果我在不久的将来找到一种更动态的方式来做到这一点,我会尝试发布它。莲花,如果你碰巧看到这是一个用你的评论回答问题的问题,我会将已回答的状态切换给你。
在这种情况下,使用 System.Runtime.CompilerServices 可以创造奇迹,从 Net4.5 开始。[参见调用者信息] 可选的 CallerMemberName 字符串可用于标识调用日志的方法/属性。
来自MSDN
Private Sub DoProcessing()
TraceMessage("Something happened.")
End Sub
Public Sub TraceMessage(message As String,
<System.Runtime.CompilerServices.CallerMemberName> Optional memberName As String = Nothing,
<System.Runtime.CompilerServices.CallerFilePath> Optional sourcefilePath As String = Nothing,
<System.Runtime.CompilerServices.CallerLineNumber()> Optional sourceLineNumber As Integer = 0)
System.Diagnostics.Trace.WriteLine("message: " & message)
System.Diagnostics.Trace.WriteLine("member name: " & memberName)
System.Diagnostics.Trace.WriteLine("source file path: " & sourcefilePath)
System.Diagnostics.Trace.WriteLine("source line number: " & sourceLineNumber)
End Sub
' Sample output:
' message: Something happened.
' member name: DoProcessing
' source file path: C:\Users\username\Documents\Visual Studio 2012\Projects\CallerInfoVB\CallerInfoVB\Form1.vb
' source line number: 15
显示属性调用结果的不同实现
Class Foo
Public ReadOnly Property ThisPropertyObject As Object
Get
LogManager.Ping
Return Nothing
End Get
End Property
Sub New()
Dim this = ThisPropertyObject
End Sub
End Class
Public Module LogManager
Public Sub Ping(<CallerMemberName> Optional memberName As String = Nothing,
<CallerFilePath> Optional sourcefilePath As String = Nothing,
<CallerLineNumber> Optional sourceLineNumber As Integer = 0)
Trace.Writeline(String.Format("[{0}]|{1}|LN:{2}] <PING>",
Path.GetFileName(sourcefilePath),
memberName,
sourceLineNumber)
)
)
End Sub
End Module
'! Results from 'DebugView'
' [20692][LogTestFile.vb|ThisPropertyObject|LN:62] <PING>