VB.NET中实现类似Obj-C的晚绑定“兜底”方法可行吗?
在VB.NET中实现未定义属性的动态处理方案
针对你的需求,.NET没有直接等同于Objective-C objc_msgSend的全局消息拦截机制,但可以通过以下几种方案实现类似效果,适配你冻结类库的场景:
方案1:使用DynamicObject实现动态属性解析
如果允许修改基类cBaseRow,可以让它继承DynamicObject,重写TryGetMember方法来捕获未定义的属性调用:
Public Class cBaseRow Inherits DynamicObject ' 保留原有的GetValue、GetNote、RowNum等成员 Protected MustOverride ReadOnly Property TabName As String ' 子类需实现表名 Public Overrides Function TryGetMember(binder As GetMemberBinder, ByRef result As Object) As Boolean Dim propName = binder.Name ' 识别以"Note"结尾的属性请求 If propName.EndsWith("Note", StringComparison.Ordinal) Then Dim fieldName = propName.Substring(0, propName.Length - 4) ' 调用GetNote返回备注 result = GetNote(TabName, RowNum, fieldName) Return True End If ' 非Note属性请求,交给原有逻辑处理 Return MyBase.TryGetMember(binder, result) End Function End Class
注意事项
- 使用时需将变量声明为
Dynamic类型,例如:Dim myRentRow As Dynamic = New RentRow(),静态类型声明不会触发动态解析逻辑。 - 子类需实现
TabName属性,提供对应的表名参数。
方案2:通过TypeDescriptionProvider实现静态属性扩展
如果希望保持原有静态类型声明不变,可通过自定义类型描述器动态注入Note属性,无需修改变量类型:
步骤1:自定义属性描述器
Public Class NotePropertyDescriptor Inherits PropertyDescriptor Private ReadOnly _fieldName As String Private ReadOnly _tabName As String Public Sub New(fieldName As String, tabName As String) MyBase.New($"{fieldName}Note", Nothing) _fieldName = fieldName _tabName = tabName End Sub Public Overrides Function GetValue(component As Object) As Object Dim row = DirectCast(component, cBaseRow) Return row.GetNote(_tabName, row.RowNum, _fieldName) End Function ' 实现其他PropertyDescriptor抽象成员(均为只读逻辑) Public Overrides ReadOnly Property PropertyType As Type = GetType(String) Public Overrides ReadOnly Property IsReadOnly As Boolean = True Public Overrides ReadOnly Property ComponentType As Type = GetType(cBaseRow) Public Overrides Function CanResetValue(component As Object) As Boolean = False Public Overrides Sub ResetValue(component As Object) Throw New NotImplementedException() End Sub Public Overrides Sub SetValue(component As Object, value As Object) Throw New NotImplementedException() End Sub Public Overrides Function ShouldSerializeValue(component As Object) As Boolean = False End Class
步骤2:注册类型描述器到子类
在每个子类的静态构造函数中注册自定义类型提供器,动态添加Note属性:
Public Class RentRow Inherits cBaseRow Shared Sub New() ' 定义当前子类对应的字段列表 Dim fieldNames = New List(Of String) From {"RentValue", "LeaseTerm"} ' 注册自定义类型提供器 Dim provider = New BaseRowTypeDescriptionProvider( TypeDescriptor.GetProvider(GetType(RentRow)), "Rent", ' 表名 fieldNames ) TypeDescriptor.AddProvider(provider, GetType(RentRow)) End Sub ' 原有属性定义 Public ReadOnly Property RentValue As Double Get Return Double.TryParse(GetValue("RentValue"), Nothing) End Get End Property End Class
优势
- 原有代码无需修改,静态类型变量可直接访问
RentValueNote属性。 - 属性会出现在智能提示中,符合原有开发习惯。
方案3:T4模板自动生成扩展方法(零侵入类库)
如果类库完全冻结无法修改,可通过T4模板批量生成扩展方法,一次性解决所有字段的Note调用需求:
<#@ template language="VB" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Reflection" #> <#@ import namespace="System.Linq" #> Imports System.Runtime.CompilerServices Public Module BaseRowNoteExtensions <# ' 遍历所有继承自cBaseRow的子类 Dim baseType = GetType(cBaseRow) For Each asm In AppDomain.CurrentDomain.GetAssemblies() Dim subTypes = asm.GetTypes().Where(Function(t) t.IsSubclassOf(baseType) AndAlso Not t.IsAbstract) For Each subType In subTypes ' 假设子类通过静态字段TabName定义表名 Dim tabName = subType.GetField("TabName")?.GetValue(Nothing)?.ToString() If tabName Is Nothing Then Continue For ' 遍历子类的所有公共属性 Dim props = subType.GetProperties(BindingFlags.Public Or BindingFlags.Instance) For Each prop In props #> <Extension()> Public Function <#= prop.Name & "Note" #>(row As <#= subType.Name #>) As String Return row.GetNote("<#= tabName #>", row.RowNum, "<#= prop.Name #>") End Function <# Next Next Next #> End Module
优势
- 完全不修改原有类库,仅需将生成的模块添加到项目中。
- 自动适配所有子类和字段,无需手动编写大量重复代码。
内容的提问来源于stack exchange,提问作者Maury Markowitz
相关产品推荐
相关产品推荐

