Свойства по умолчанию в VB.NET?
В начале .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 Framework есть понятие члена по умолчанию. Ключевыми компонентами являются класс 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.
С# имеет точные правила, но не допускает такой же гибкости. Вероятно, вы видели индексатора раньше:
public class Sample {
public int this[int index] {
get { return index; }
}
}
Этот код также позволяет компилятору вывести атрибут [DefaultMember]. Именованное свойство в этом атрибуте - "Item". И поэтому вы видите, что индексщик документально и индексирован в библиотеке MSDN как "Item".
Ответ 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
В этом примере он вытягивает объект, но не преобразовывает его в целое.
Брайан, я не понимаю, почему ваш желаемый эффект не может быть достигнут с помощью Widening Operator CType
. Код, который вы нам показали, можно заставить работать. Однако остерегайтесь неявных преобразований. Обычно это не очень хорошая идея.
Ответ 4
Нет, это было явно удалено из VB 7.
Если у вас длинная цепочка свойств по умолчанию, то точно знать, что будет возвращено, сложно. Если b
и c
имеют метод Foo
, то a.Foo(1)
означает a.b.Foo(1)
или a.b.c.Foo(1)
?
Настоящий кикер был Set
. Отбрасывая свойства по умолчанию, они также могли отбросить ключевое слово Set
для назначения объекта.
Ответ 5
Существует атрибут DefaultProperty
, поэтому ваш пример должен быть правильным, но это похоже на компоненты, которые используются в Windows Forms desinger.
Ответ 6
Вы можете переопределить метод ToString для вывода значения как строки, так что когда вы выполняете Response.Write(MyObject), вы получите тот же эффект.
Public Overrides Function ToString() As String
Return Me.Value.ToString
End Function
[EDIT] Теперь, когда я понимаю это лучше, почему бы просто не дать способ напрямую получить значения содержащихся объектов.
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
Ответ 7
Чтобы ответить на мой собственный вопрос, перегрузка операторов показалась здесь интересным решением.
В конце концов, это было не очень удобно.
В итоге мне пришлось добавить около 17 1-строчных методов, что означало много места для ошибок. Более важно то, что вы не можете перегрузить оператор присваивания; перегрузка для =
предназначена только для тестирования равенства.
Так что даже с этим я не могу сказать:
Dim x as Integer = MyObject.Stats(Stat.Health)
... В этом примере он вытягивает объект, но не преобразует его в целое число, поэтому, конечно, результат является исключением.
Мне действительно нужна хорошая старомодная собственность по умолчанию, но я думаю, что эти дни закончились.
Ответ 8
Привет, Джон, ваш ответ был очень полезен! Я изменил для использования с любым типом, спасибо.
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
Ответ 9
Я искал ответ на подобную проблему, и в процессе я наткнулся на это здесь.
На самом деле Джон ответил мне в направлении, в котором мне нужно было идти. И это может помочь и с исходным вопросом:
Моя проблема:
Мне нужно что-то, что я мог бы использовать так же, как Integer
Dim myVal as Integer
myVal = 15
If myVal = 15 then
...
End If
... и так далее...
Однако мне нужны были и другие вещи.
myVal.SomeReadOnlyProperty (as String)
myVal.SomeOtherReadOnlyProperty (as Integer)
(на самом деле эти readonly Properties также могут быть методами...)
и т.д. Поэтому мне действительно нужен объект
Я думал о методах расширения для Integer (@_ @)... Я не хотел идти по этой дороге...
Я также думал о написании "ReadOnlyPropertyOracle" как отдельного класса и давал ему методы вроде
GetSomeReadOnlyProperty(ByVal pVal as Integer) as String
GetSomeOtherReadOnlyProperty(ByVal pVal as Integer) as Integer
weeeell.... Это сработало бы, но выглядело ужасно...
Итак, Джон Харк и Брайан Маккей прокомментировали оператора:
Сочетание как операторов расширения/сужения преобразования (для присвоения), так и операторов сравнения для... хорошо сравнения.
Вот часть моего кода, и он делает то, что мне нужно:
'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 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 десятка однолинейных определений операторов, но большинство из них тривиально с небольшим количеством места для ошибки;-) Так что это работает для меня...
Ответ 10
Вы можете использовать этот атрибут, если импортируете System.ComponentModel.
Как уже упоминалось, это не идеально, поскольку VB.Net предпочитает использовать атрибут Default. Конечно, это связано с условиями, которые на самом деле не помогают (например, для индекса).
Но если вы используете
Imports System.ComponentModel
он позволяет использовать атрибут в вашем классе.