tags:

views:

2946

answers:

6

In the early days of .Net, I believe there was an attribute you could decorate a class with to specify a default property.

According to some articles I've found, this appears to have been yanked from the framework at some point because it was a little confusing, and I can see how that is the case.

Still, is there another way to get the functionality it provided?

It looked something like this:

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

This allowed you to do: Response.Write(MyObject) instead of Response.Write(MyObject.Value) ...This is not a terribly clunky example, but in some complex OO contexts it gets a little hideous. Please let me know if there is a better way.

Note: I am not looking for the Default keyword, which can only be used on properties that take a parameter.

Thanks in advance!

A: 

There is a DefaultProperty attribute so your example should be correct, but this seems to be for components which are used in the Windows Forms desinger.

hangy
I found that, but it doesn't seem to do what I'm looking for.
Brian MacKay
Which is to say, I actually tried it out and it seemed to have no impact at all. :)
Brian MacKay
Yes, DefaultProperty is really only useful to a propertygrid and is what allows it to determine if the current value is different than the default value.
Scott Dorman
+1  A: 

You could override the ToString method to output Value as a string so that when you do Response.Write(MyObject), you get the same effect.

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

[EDIT] Now that I understand it better, why not just provide a way to get directly at the values of the contained objects.

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
tvanfosson
Interesting idea - if I could get the variable to come out as an integer or an object, that would be even better...
Brian MacKay
Response.Write is going to call ToString on the object anyway when it writes it to the response stream.
tvanfosson
I'm not actually doing response.write though - actual usage is more like: thisObject.stats(stat.health).value, see? ugly.
Brian MacKay
Here's what it really comes down to: I want ThisObject.Stats(Stat.Health) to just return an integer via a default property, but I also need to be able to get into other methods (ThisObject.Stats(Stat.Health).OtherMethod()). ThisObject.Stats(Stat.Health).Value is just a lot of periods - clunky.
Brian MacKay
Maybe I'm asking too much, eh? This is really an exercise in designing a good API for me.
Brian MacKay
A: 

To answer my own question, operator overloading seemed to be an interesting solution here.

In the end, it wasn't a good fit.

I ended up having to add in about 17 1-line methods, which means lots of room for bugs. More important is that you can't overlaod the assignment operator; the overload for = is for equality testing only.

So even with this, I can't say:

Dim x as Integer = MyObject.Stats(Stat.Health) ...In this example it pulls the object, but does not convert it to an integer, so of course the result is an exception.

What I really need is a good old fashioned default property, but I think those days are over.

Brian MacKay
+3  A: 

Well, the .NET framework does have a notion of a default member. Key ingredients are the DefaultMemberAttribute class and Type.GetDefaultMembers(). In VB.NET, specifying the default member is part of the language syntax:

  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

Use it like this:

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

The compiler implements this by emitting [DefaultMember] automatically. This however has a restriction, the property must have an index argument, specifically to avoid the syntax ambiguity. This restriction is not enforced when specifying the attribute explicitly:

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

But that default member would only be accessible as a default by a language that allows such syntax. For which I don't know an example.

C# has the exact same rules, but doesn't allow the same kind of flexibility. You've probably seen the indexer before:

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

This code also makes the compiler output the [DefaultMember] attribute. The named property in that attribute is "Item". And that's why you see the indexer documented and indexed in the MSDN Library as "Item".

Hans Passant
Interesting comment. It doesn't solve my problem, but at least it gives me some background on the situation - thanks!
Brian MacKay
If it doesn't answer your question why did you mark it as the correct answer?
Bryan Anderson
Because the answer to my problem is: you can't do it. This guy provided the best explanation of why it won't work.
Brian MacKay
+2  A: 

No, that was explicitly removed from VB 7.

When you have a long chain of default properties, knowing exactly what will be returned is hard. When both b and c have a Foo method, does a.Foo(1) mean a.b.Foo(1) or a.b.c.Foo(1)?

The real kicker was Set. By dropping default properties, they were also able to drop the Set keyword for object assignment.

Jonathan Allen
C++ actually has a similar problem due to the fact that `operator ->` can be overloaded: It will always call nested `operator ->`s greedily until it gets to a real pointer or an object that doesn't overload it.
Konrad Rudolph
+1  A: 

In this example it pulls the object, but does not convert it to an integer.

Brian, I don't see why your desired effect cannot be achieved using a Widening Operator CType. The code you showed us can be made to work. However, beware of implicit conversions. They're generally not a good idea.

Konrad Rudolph