投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/24 11:45:25
> 『式木』から辿るというのは如何でしょう。

ということで、式木を用いた実装案です。

Debug.Print(GetDisplayName(Function() ClassA.Alpha)) 'Shared プロパティ  
Debug.Print(GetDisplayName(Sub() Me.SubProc())) 'インスタンス Sub メソッド  
Debug.Print(GetDisplayName(Function() Me.FuncProc())) 'インスタンス Function メソッド  
Dim cOleDb As New System.Data.OleDb.OleDbConnectionStringBuilder()
Debug.Print(GetDisplayName(Function() cOleDb.DataSource)) 'インスタンス プロパティ  
Debug.Print(GetDisplayName(Function() cOleDb.FileName))
Debug.Print(GetDisplayName(Function() cOleDb.OleDbServices))
Debug.Print(GetDisplayName(Function() cOleDb.PersistSecurityInfo))


Public Shared Function GetDisplayName(Of T)(proc As Expression(Of T)) As String
    If proc Is Nothing OrElse TypeOf proc.Body Is ConstantExpression Then
        Return Nothing
    End If
    Dim m = TryCast(proc.Body, MemberExpression)        'フィールドやプロパティ  
    Dim c = TryCast(proc.Body, MethodCallExpression)    'メソッド  
    Dim atr As DisplayNameAttribute = Nothing
    If m IsNot Nothing Then
        atr = m.Member.GetCustomAttributes(Of DisplayNameAttribute)().FirstOrDefault()
        If atr Is Nothing AndAlso m.Member.MemberType = MemberTypes.Property Then
            'プロパティには付与されていなかったが、getter や setter には付与されている可能性がある  
            Dim getter = TryCast(m.Member, PropertyInfo)?.GetGetMethod(False)
            'Dim setter = TryCast(m.Member, PropertyInfo)?.GetSetMethod(False)  
            If getter IsNot Nothing Then
                atr = getter.GetCustomAttributes(Of DisplayNameAttribute)().FirstOrDefault()
            End If
        End If
    ElseIf c IsNot Nothing Then
        atr = c.Method.GetCustomAttributes(Of DisplayNameAttribute)().FirstOrDefault()
        If atr Is Nothing AndAlso c.Method.IsSpecialName AndAlso c.Method.Name.StartsWith("get_"Then
            '引数付きプロパティの getter が渡されたら、親プロパティの属性もさぐる  
            Dim propName = c.Method.Name.Substring(4)
            Dim full = BindingFlags.Instance Or BindingFlags.Static Or BindingFlags.Public Or BindingFlags.NonPublic
            Dim prop = c.Method.ReflectedType.GetProperties(full) _
                .Where(Function(p) p.Name = propName AndAlso p.GetGetMethod() Is c.Method) _
                .FirstOrDefault()
            atr = prop?.GetCustomAttributes(Of DisplayNameAttribute)()?.FirstOrDefault()
        End If
    End If
    Return atr?.DisplayName
End Function


🔹メリット
• 元の方式だと、GetDisplayName(Of ClassA)(NameOf(ClassB.Prop)) のような指定ミスがあり得るが、ラムダ式を渡せば、そうした心配がない
• オーバーロードを持つメンバーに対しても、どの引数定義を指定しているのかを正しく引き渡せる

🔸デメリット
• イベントの DisplayName を得ることができない
• WriteOnly プロパティを渡すことができない