【问题标题】:How do you get the name of a property using the property itself如何使用属性本身获取属性的名称
【发布时间】:2013-11-27 15:59:06
【问题描述】:

我遇到了一个问题,我需要获取属性的名称以进行日志记录。我确信在 VB.Net 中有一种方法可以使用反射和 lambda 表达式的组合来做到这一点,但到目前为止我还没有成功。

我想要做的是转换这个:

objAddress.AddressLine

到这里:

"AddressLine"

【问题讨论】:

  • 你怎么知道你需要的是 objAddress.AddressLine 属性?如果它总是相同的属性,你可以在类 ex 中添加一个常量:ADDRESSLINE_PROPERTYNAME = "AddressLine"
  • 这是真的。我希望确保此日志记录始终保持动态,如果团队中的其他程序员出于任何原因碰巧更改了属性(这将违反打开/关闭原则,但是我团队中的其他程序员不遵循 SOLID 实践)。
  • 如果找不到动态的方式,一定要在属性上写cmets说“如果你改了名字,别忘了改常量”。
  • 这是一个很好的建议,莲花。如果归结为使用常量,那是我必须要做的。
  • 您也可以定义一个自定义属性来定义 LogName:<LoggingNameAttribute("Address Line")>Property AddressLine... 这是否有价值或可以工作取决于记录器如何确定要记录的内容...即代码你没有表现出来。我怀疑最简单的方法(缺少常量)是让/所有类简单地有一种基于键返回词汇的方法。

标签: vb.net reflection properties lambda


【解决方案1】:

过去,我使用了一种在网上找到的方法,即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 功能(有关此功能以及其他新功能的详细信息,请参见Here)。

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");
        }
    }

【讨论】:

    【解决方案2】:

    对于仍在寻找优雅解决方案的人,在最新版本的 VB 和 C# 中,您可以使用“nameof()”。

    C#:

    var propertyName = nameof(objAddress.AddressLine)
    

    VB .NET:

    Dim propertyName = nameof(objAddress.AddressLine)
    

    请注意,如果您没有对象的实例,您可以只使用它的类名。

    【讨论】:

      【解决方案3】:

      这将显示当前的方法。您可能需要替换属性名称的“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)
      

      【讨论】:

        【解决方案4】:

        来自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 
        

        【讨论】:

          【解决方案5】:

          我决定采用 the_lotus 对我最初的问题的回答,即使用常量。如果我在不久的将来找到一种更动态的方式来做到这一点,我会尝试发布它。 lotus,如果你碰巧看到这是一个用你的评论回答问题的问题,我会把已回答的状态切换给你。

          【讨论】:

            【解决方案6】:

            在这种情况下,使用 System.Runtime.CompilerServices 可以创造奇迹,从 Net4.5 开始。 [参见Caller Information] 可选的 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> 
            

            【讨论】:

              猜你喜欢
              • 2018-08-30
              • 1970-01-01
              • 2018-11-16
              • 1970-01-01
              • 1970-01-01
              • 2023-03-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多