2008-11-15 27 views
26

En los primeros días de .NET, creo que había un atributo con el que se podía decorar una clase para especificar una propiedad predeterminada.¿Propiedades predeterminadas en VB.NET?

De acuerdo con algunos artículos que he encontrado, esto parece haber sido eliminado del marco en algún momento, porque fue un poco confuso, y puedo ver cómo es ese el caso.

¿Todavía hay otra forma de obtener la funcionalidad que proporciona?

Parecía algo como esto:

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

Esto permitió que hagas Response.Write(MyObject) en lugar de Response.Write(MyObject.Value) ... Esto no es un ejemplo terriblemente torpe, pero en algunos contextos orientados a objetos complejos se pone un poco horrible . Por favor, avíseme si hay una mejor manera.

Nota: No estoy buscando la palabra clave predeterminada, que solo se puede usar en las propiedades que toman un parámetro.

Respuesta

28

Bueno, el .NET framework tiene una noción de un miembro predeterminado. Los ingredientes clave son la clase DefaultMemberAttribute y Type.GetDefaultMembers(). En VB.NET, especificando el miembro predeterminado es parte de la sintaxis del lenguaje:

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 

utilizar de esta manera:

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

El compilador implementa esta emitiendo [DefaultMember] automáticamente.Sin embargo, esto tiene una restricción, la propiedad debe tener un argumento de índice, específicamente para evitar la ambigüedad de la sintaxis. Esta restricción no se aplica cuando se especifica el atributo explícitamente:

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

Pero ese miembro predeterminado sólo sería accesible como un incumplimiento por parte de un lenguaje que permite tales sintaxis. Para lo cual no conozco un ejemplo en .NET, esto se usó en los días COM, como VB6. También la razón principal detrás de que VB6 tenga la palabra clave Establecer, resuelve la ambigüedad y dice "Me refiero al objeto, no a la propiedad predeterminada del objeto". Detalle de sintaxis muy doloroso para muchos programadores principiantes de Visual Basic en aquel entonces.

C# tiene exactamente las mismas reglas, pero no permite el mismo tipo de flexibilidad. Usted probablemente ha visto antes el indexador:

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

Este código también hace que la salida del compilador el atributo [DefaultMember]. La propiedad nombrada en ese atributo es "Artículo". Y es por eso que ve el indexador documentado e indexado en MSDN Library como "Artículo".

+0

Interesante comentario. No resuelve mi problema, pero al menos me da algunos antecedentes sobre la situación, ¡gracias! –

+0

Si no responde su pregunta, ¿por qué la marcó como la respuesta correcta? –

+1

Porque la respuesta a mi problema es: no puedes hacerlo. Este tipo brindó la mejor explicación de por qué no funcionará. –

1

No es un atributo DefaultProperty para que su ejemplo debería ser correcta, pero esto parece ser para los componentes que se utilizan en las formas desinger de Windows.

+0

Lo encontré, pero no parece hacer lo que estoy buscando. –

+0

Lo que quiere decir, en realidad lo probé y parecía no tener ningún impacto en absoluto. :) –

+2

Sí, DefaultProperty realmente solo es útil para una propiedad de cuadrícula y es lo que le permite determinar si el valor actual es diferente al valor predeterminado. –

1

Puede anular el método ToString para mostrar el valor como una cadena para que cuando haga Response.Write (MyObject), obtenga el mismo efecto.

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

[EDIT] Ahora que entiendo mejor, ¿por qué no proporcionan una manera de conseguir directamente en los valores de los objetos que contiene.

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 
+0

Idea interesante: si pudiera hacer que la variable salga como un entero o un objeto, sería aún mejor ... –

+0

Response.Write va a llamar a ToString en el objeto de todos modos cuando lo escriba en el flujo de respuesta. – tvanfosson

+0

En realidad, no estoy haciendo response.write: el uso real es más parecido a: thisObject.stats (stat.health) .value, ¿ves? feo. –

1

Para responder a mi propia pregunta, la sobrecarga del operador parecía ser una solución interesante aquí.

Al final, no era una buena opción.

Terminé teniendo que agregar aproximadamente 17 métodos de 1 línea, lo que significaba mucho espacio para errores. Más importante es que no puede sobrecargar el operador de asignación; la sobrecarga para = es solo para pruebas de igualdad.

Así que, con esto, no se puede decir:

Dim x as Integer = MyObject.Stats(Stat.Health) ... En este ejemplo se saca el objeto, pero no lo convierte en un entero, por lo que, por supuesto, el resultado es una excepción.

Lo que realmente necesito es una buena propiedad predeterminada pasada de moda, pero creo que esos días han terminado.

4

No, que fue eliminado explícitamente de VB 7.

Cuando usted tiene una larga cadena de propiedades por defecto, sabiendo exactamente lo que va a ser devuelto es duro. Cuando ambos b y c tienen un método Foo, ¿significa a.Foo(1)a.b.Foo(1) o a.b.c.Foo(1)?

El verdadero truco fue Set. Al eliminar las propiedades predeterminadas, también podían soltar la palabra clave Set para la asignación de objetos.

+0

C++ en realidad tiene un problema similar debido al hecho de que 'operator ->' puede estar sobrecargado: Siempre llamará 'operator '>' anidado 's codiciosamente hasta que llegue a un puntero real o un objeto que no lo sobrecargue. –

5

En este ejemplo, tira del objeto, pero no lo convierte en un número entero.

Brian, no veo por qué no se puede lograr el efecto deseado con un Widening Operator CType. El código que nos mostró puede hacerse funcionar. Sin embargo, ten cuidado con las conversiones implícitas. Generalmente son no una buena idea.

0

Aún puede usar el atributo si importa System.ComponentModel.

Como han mencionado otros, esto no es ideal ya que VB.Net prefiere que use el atributo Predeterminado. Por supuesto, eso viene con condiciones, lo que realmente no ayuda (requiere un índice, por ejemplo).

Pero si utiliza

Imports System.ComponentModel

que le permite utilizar el atributo en su clase.

8

He descubierto que se puede hacer exactamente lo que el cartel original quería utilizar Widening Operator CType Esto fue mencionado anteriormente, pero sin mucho detalle, así que perdí por completo ya que estaba tratando de encontrar una respuesta a esta pregunta. Esta metodología no define una propiedad predeterminada, per se, pero logra el mismo resultado.

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 

Así que ahora

Dim MyObject as GenericStat 
MyObject = 123 

y

Dim Int as Integer 
Int = MyObject 

tanto el trabajo sin la referencia .Value y sin un indexador como myobject(1)

+0

¡Interesante, tendré que probar esto! –

1

Hola Juan su respuesta fue muy útil! Cambié para usar con cualquier tipo, gracias.

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 

Y el uso:

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 
2

He estado buscando una respuesta a un problema similar y en el proceso me encontré con este aquí. En realidad, la respuesta de John me indicó la dirección a la que tenía que ir. Y podría ayudar con la pregunta original, así:

Mi Problema: que necesitaba algo que podría utilizar como un entero

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

... y así sucesivamente ... Sin embargo yo necesitaba cosas adicionales, así

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

(en realidad esas propiedades podrían ser de sólo lectura Métodos así ...)
etc ... así que realmente se necesita un objeto

Estaba pensando en métodos de extensión de Entero (@ _ @) ... Yo no quiero ir por ese camino ...

También pensé en escribir un "ReadOnlyPropertyOracle" como una clase separada y darle métodos como

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

weeeell .... Eso hubiera funcionado pero se veía espantosa ...

Así llegaron en Hack de Juan y el comentario de Brian MacKay acerca de los operadores: la combinación de ambos, ampliación/conversión de restricción operadores (para asignación) y operación de comparación rs para ... bien la comparación. aquí es parte de mi código y lo hace lo que necesito:

'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 

Sí, esto seguirá siendo 1-2 docena de definiciones operador de una sola línea, pero la mayoría de ellos son triviales con poco margen de error ;-) Así que esto funciona para mí ...

+0

¡Gracias por la contribución aquí! Parece que has extendido la idea de John, y para parafrasear lo que dijo Hans sobre eso en otra respuesta, esto puede funcionar, pero es un poco complicado. Por mi parte, he llegado a la conclusión de que si tienes que esforzarte tanto para que esto suceda, realmente estamos trabajando en contra de C# y probablemente deberíamos encontrar otra manera. Pero es genial que encuentres una forma. –

Cuestiones relacionadas