VB.NET 中的默认属性?

Posted

技术标签:

【中文标题】VB.NET 中的默认属性?【英文标题】:Default properties in VB.NET? 【发布时间】:2010-09-22 12:50:04 【问题描述】:

在 .NET 的早期,我相信有一个属性可以用来装饰类以指定默认属性。

根据我发现的一些文章,这似乎已在某个时候从框架中删除,因为它有点令人困惑,我可以看到情况如何。

还有其他方法可以获取它提供的功能吗?

看起来像这样:

<DefaultProperty("Value")> _  
Public Class GenericStat
    ...
    Public Property Value() As Integer
        ...
    End Property
    ...
End Class

这使您可以使用Response.Write(MyObject) 而不是Response.Write(MyObject.Value)... 这不是一个非常笨拙的示例,但在一些复杂的面向对象的上下文中它会变得有点可怕。请让我知道是否有更好的方法。

注意:我不是在寻找 Default 关键字,它只能用于带参数的属性。

【问题讨论】:

【参考方案1】:

嗯,.NET 框架确实有一个默认成员的概念。关键成分是 DefaultMemberAttribute 类和 Type.GetDefaultMembers()。在 VB.NET 中,指定默认成员是语言语法的一部分:

  Public Class Sample
    Private mValue As Integer
    Default Public ReadOnly Property Test(ByVal index As Integer) As Integer
      Get
        Return index
      End Get
    End Property
  End Class

像这样使用它:

  Sub Main()
    Dim s As New Sample
    Console.WriteLine(s(42))
    Console.ReadLine()
  End Sub

编译器通过自动发出 [DefaultMember] 来实现这一点。然而,这有一个限制,该属性必须有一个索引参数,特别是为了避免语法歧义。明确指定属性时不强制执行此限制:

  <System.Reflection.DefaultMember("AnotherTest")> _
  Public Class Sample
    Public ReadOnly Property AnotherTest() As Integer
      Get
        Return 42
      End Get
    End Property
  End Class

但是默认成员只能由允许这种语法的语言默认访问。我不知道 .NET 中的示例,这是在 COM 时代使用的,例如 VB6。也是 VB6 使用 Set 关键字的核心原因,它解决了歧义并声明“我的意思是对象,而不是对象的默认属性”。对当时的许多初级 Visual Basic 程序员来说,语法细节非常痛苦。

C# 具有完全相同的规则,但不允许具有相同的灵活性。您可能以前见过索引器:

  public class Sample 
    public int this[int index] 
      get  return index; 
    
  

此代码还使编译器输出 [DefaultMember] 属性。该属性中的命名属性是“Item”。这就是为什么您在 MSDN 库中看到索引器被记录和索引为“项目”的原因。

【讨论】:

有趣的评论。它并没有解决我的问题,但至少它给了我一些关于这种情况的背景 - 谢谢! 如果它没有回答您的问题,您为什么将其标记为正确答案? 因为我的问题的答案是:你做不到。这个人提供了为什么它不起作用的最好解释。 @BrianMacKay 'John' 提供的答案确实有效,我认为你应该更改接受的答案 这是一个黑客行为,任何理智的程序员都不应该把它放在他的代码中。【参考方案2】:

我发现您可以使用Widening Operator CType 完全按照原始发帖人的要求进行操作 上面提到了这一点,但没有太多细节,所以当我试图找到这个问题的答案时,我完全错过了它。这种方法本身并没有定义默认属性,但它实现了相同的结果。

Public Class GenericStat
    ...
    Public Property Value() As Integer
    ...
    End Property
    ...
    'this could be overloaded if needed
    Public Sub New(ByVal Value As Integer)
        _Value = Value
    End Sub
    '
    Public Shared Widening Operator CType(ByVal val As Integer) As GenericStat
        Return New GenericStat(val)
    End Operator
    '
    Public Shared Widening Operator CType(ByVal val As GenericStat) As Integer
        Return val.Value
    End Operator
End Class

现在

Dim MyObject as GenericStat
MyObject = 123

Dim Int as Integer
Int = MyObject   

两者都可以在没有 .Value 引用且没有索引器(例如 myobject(1))的情况下工作

【讨论】:

有趣,我得测试一下!【参考方案3】:

在此示例中,它会拉取对象,但不会将其转换为整数。

Brian,我不明白为什么使用Widening Operator CType 无法达到您想要的效果。您向我们展示的代码可以运行。但是,请注意隐式转换。它们通常不是一个好主意。

【讨论】:

【参考方案4】:

不,它已从 VB 7 中明确删除。

当您拥有一长串默认属性时,很难确切知道将返回什么。当bc 都有Foo 方法时,a.Foo(1) 是指a.b.Foo(1) 还是a.b.c.Foo(1)

真正的踢球者是Set。通过删除默认属性,他们还能够删除用于对象分配的 Set 关键字。

【讨论】:

C++ 实际上也有类似的问题,因为operator -&gt; 可以被重载:它总是会贪婪地调用嵌套的operator -&gt;s,直到它得到一个真正的指针或一个没有的对象超载它。【参考方案5】:

我一直在寻找类似问题的答案,在此过程中我偶然发现了这个问题。 实际上,约翰的回答为我指明了我需要去的方向。它也可能有助于解决原始问题:

我的问题: 我需要一些可以像整数一样使用的东西

Dim myVal as Integer
myVal = 15
If myVal = 15 then
  ...
End If

...等等... 但是我还需要额外的东西

myVal.SomeReadOnlyProperty (as String)
myVal.SomeOtherReadOnlyProperty (as Integer)

(实际上那些只读属性也可以是方法......) 等等...所以我真的需要一个对象

我正在考虑 Integer (@_@) 的扩展方法……我不想走那条路……

我也想过写一个“ReadOnlyPropertyOracle”作为一个单独的类,并给它类似的方法

GetSomeReadOnlyProperty(ByVal pVal as Integer) as String
GetSomeOtherReadOnlyProperty(ByVal pVal as Integer) as Integer

weeeell .... 这本来可以,但看起来很可怕...

于是出现了 John's Hack 和 Brian MacKay 对运营商的评论: 结合扩大/缩小转换运算符(用于赋值)和比较运算符用于...以及比较。 这是我的代码的一部分,它可以满足我的需要:

'The first two give me the assignment operator like John suggested
Public Shared Widening Operator CType(ByVal val As Integer) As MySpecialIntType
    Return New MySpecialIntType(val)
End Operator

'As opposed to John's suggestion I think this should be Narrowing?
Public Shared Narrowing Operator CType(ByVal val As MySpecialIntType) As Integer
    Return val.Value
End Operator

'These two give me the comparison operator
'other operators can be added as needed
Public Shared Operator =(ByVal pSpecialTypeParameter As MySpecialIntType, ByVal pInt As Integer) As Boolean
    Return pSpecialTypeParameter.Value = pInt
End Operator

Public Shared Operator <>(ByVal pSpecialTypeParameter As MySpecialIntType, ByVal pInt As Integer) As Boolean
    Return pSpecialTypeParameter.Value <> pInt
End Operator

是的,这仍然是 1-2 打单行运算符定义,但其中大多数都是微不足道的,几乎没有出错的余地 ;-) 所以这对我有用...

【讨论】:

感谢您的贡献!看起来您已经扩展了约翰的想法,并且用汉斯在另一个答案中所说的话来解释,这可能有效,但它有点像黑客!就我而言,我得出的结论是,如果您必须努力工作才能实现这一目标,那么我们实际上是在反对 C#,并且可能应该找到另一种方法。但是很酷,你找到了方法。【参考方案6】:

为了回答我自己的问题,运算符重载似乎是一个有趣的解决方案。

最终,这并不合适。

我最终不得不添加大约 17 个 1 行方法,这意味着有很多错误空间。更重要的是不能重载赋值运算符; = 的重载仅用于相等性测试。

即使这样,我也不能说:

Dim x as Integer = MyObject.Stats(Stat.Health) ...在这个例子中它会拉取对象,但不会将其转换为整数,所以结果当然是异常。

我真正需要的是一个好的老式默认属性,但我认为那些日子已经过去了。

【讨论】:

【参考方案7】:

有一个DefaultProperty 属性,所以您的示例应该是正确的,但是这似乎是针对在 Windows 窗体设计器中使用的组件。

【讨论】:

我发现了,但它似乎没有达到我想要的效果。 也就是说,我真的试过了,好像一点影响都没有。 :) 是的,DefaultProperty 实际上只对 propertygrid 有用,它允许它确定当前值是否不同于默认值。【参考方案8】:

您可以重写 ToString 方法以将 Value 作为字符串输出,这样当您执行 Response.Write(MyObject) 时,您将获得相同的效果。

    Public Overrides Function ToString() As String
        Return Me.Value.ToString 
    End Function

[编辑] 既然我理解得更好了,为什么不直接提供一种方法来直接获取所包含对象的值。

Public Class MyClass
    Private m_Stats(100) As Stats  ' or some other collection'

    Public Property StatValue(ByVal stat_number As Integer) As _
        Integer
        Get
            Return m_Stats(stat_number)
        End Get
        Set(ByVal Value As Integer)
            m_Stats(stat_number) = Value
        End Set
    End Property
End Class

【讨论】:

有趣的想法 - 如果我可以让变量以整数或对象的形式出现,那就更好了...... Response.Write 将在对象写入响应流时调用 ToString。 我实际上并没有做 response.write - 实际使用更像:thisObject.stats(stat.health).value,看到了吗?丑陋。 真正归结为:我希望 ThisObject.Stats(Stat.Health) 通过默认属性返回一个整数,但我还需要能够使用其他方法(ThisObject .Stats(Stat.Health).OtherMethod())。 ThisObject.Stats(Stat.Health).Value 只是很多时期 - 笨重。 也许我要求太多了,嗯?这真的是为我设计一个好的 API 的练习。【参考方案9】:

嗨,约翰,您的回答非常有用!我更改为使用任何类型,谢谢。

Public Class GenericStat(Of Ttype)

Public Property Value As Ttype
'
Public Sub New()

End Sub
'
'this could be overloaded if needed
Public Sub New(ByVal Value As Ttype)
   _Value = Value
End Sub
'
Public Shared Widening Operator CType(ByVal val As Ttype) As GenericStat(Of Ttype)
   Return New GenericStat(Of Ttype)(val)
End Operator
'
Public Shared Widening Operator CType(ByVal val As GenericStat(Of Ttype)) As Ttype
   Return val.Value
End Operator

End Class

及用法:

Dim MyInteger As GenericStat(Of Integer)
MyInteger = 123

Dim Int As Integer
Int = MyInteger

Dim MyString As GenericStat(Of String)
MyString = "MyValue"

Dim Str As String
Str = MyString

【讨论】:

【参考方案10】:

如果您导入 System.ComponentModel,您仍然可以使用该属性。

正如其他人所提到的,这并不理想,因为 VB.Net 更喜欢您使用 Default 属性。当然,这是附带条件的,这并没有真正的帮助(例如,需要索引)。

但是如果你使用

Imports System.ComponentModel

它允许你在你的类上使用该属性。

【讨论】:

以上是关于VB.NET 中的默认属性?的主要内容,如果未能解决你的问题,请参考以下文章

vb.net - 如何将今天设置为时间选择器的默认日期?

如何在 vb.net 中将 datagridview 单元格样式从默认文本框更改为组合框?

VB.NET本地化,检测未翻译文本

vb.net 中的快速 OCR [关闭]

VB.NET的dim语句的区别

VB.NET Winform的一些功能实现