Home All Groups Group Topic Archive Search About

Custom Attributes, Shared methods, Derived classes and reflection

Author
30 Jul 2006 7:36 PM
Jeff Mason
Hi,

I'm having a reflection brain fog here, perhaps someone can set me on the right
track.

I'd like to define a custom attribute to be used in a class hierarchy.

What I want to do is to have an attribute which can be applied to a class definition
of a class which inherits from a base, mustinherit class.  I want to define methods
in the base class which will access the contents of the attribute as it is applied to
a particular derived class.

This was reasonably easy to do when dealing with an instance of the derived class.
But, I need to be able to have these methods in the base class be shared, so the
attribute can be accessed as defined on the derived class, rather than on an instance
of the class.

That is, for the instance method I define a derived class with my custom attribute
as, say:

    <MyCustomAttribute("String1", "String2")> _
    Public MyDerivedClass
       Inherits MyBaseClass
    ...

Then, in the base class, I defined a method like:

    Public ReadOnly Property ValueFromMyAttribute() as string
       Get
              Dim classtype As Type = Me.GetType
              Dim attr As EntityAttribute =
        DirectCast(Attribute.GetCustomAttribute(classtype, _           
        GetType(MyCustomAttribute)), MyCustomAttribute)
          If attr IsNot Nothing Then
                Return attr.PropertyForString1
              Else
                Return classtype.Name
              End If
           End Get
    End Property

This seemed to work.

But, the requirement now exists to access the attribute's properties from SHARED
methods, since the values may be needed even if no instance of the derived class
exists.

It seems like this ought to be possible, but how?  If I make the property shared,
then obviously there is no instance for "Me" to refer to, so this generates a compile
error.  How can I get the type of the derived CLASS instead of from an instance of
that class?  I assume if I can get that, I can track down the attribute and access
its properties as I did above.

Thanks,

  -- Jeff

Author
31 Jul 2006 8:54 AM
tommaso.gastaldi
Hi Jeff,

if the problem were just to obtain the type you could use
GetType(YourBaseClass)  ...
but I am a little perplexed as it does not seem to make sense to have
attributes
detached from an instance.

Can you make an example where this can be meaningful. And why you need
to
use attributes to do that ?

-tom

Jeff Mason ha scritto:

Show quoteHide quote
> Hi,
>
> I'm having a reflection brain fog here, perhaps someone can set me on the right
> track.
>
> I'd like to define a custom attribute to be used in a class hierarchy.
>
> What I want to do is to have an attribute which can be applied to a class definition
> of a class which inherits from a base, mustinherit class.  I want to define methods
> in the base class which will access the contents of the attribute as it is applied to
> a particular derived class.
>
> This was reasonably easy to do when dealing with an instance of the derived class.
> But, I need to be able to have these methods in the base class be shared, so the
> attribute can be accessed as defined on the derived class, rather than on an instance
> of the class.
>
> That is, for the instance method I define a derived class with my custom attribute
> as, say:
>
>     <MyCustomAttribute("String1", "String2")> _
>     Public MyDerivedClass
>        Inherits MyBaseClass
>     ...
>
> Then, in the base class, I defined a method like:
>
>     Public ReadOnly Property ValueFromMyAttribute() as string
>        Get
>               Dim classtype As Type = Me.GetType
>               Dim attr As EntityAttribute =
>         DirectCast(Attribute.GetCustomAttribute(classtype, _
>         GetType(MyCustomAttribute)), MyCustomAttribute)
>           If attr IsNot Nothing Then
>                 Return attr.PropertyForString1
>               Else
>                 Return classtype.Name
>               End If
>            End Get
>     End Property
>
> This seemed to work.
>
> But, the requirement now exists to access the attribute's properties from SHARED
> methods, since the values may be needed even if no instance of the derived class
> exists.
>
> It seems like this ought to be possible, but how?  If I make the property shared,
> then obviously there is no instance for "Me" to refer to, so this generates a compile
> error.  How can I get the type of the derived CLASS instead of from an instance of
> that class?  I assume if I can get that, I can track down the attribute and access
> its properties as I did above.
>
> Thanks,
>
>   -- Jeff
Author
31 Jul 2006 11:29 AM
Jeff Mason
On 31 Jul 2006 01:54:21 -0700, tommaso.gasta***@uniroma1.it wrote:

>Hi Jeff,
>
>if the problem were just to obtain the type you could use
>GetType(YourBaseClass)  ...

I think I tried GetType(YourBaseClass), but it returned the type of the Base class
instead of the derived class.  The derived class is where the attribute is located.

>but I am a little perplexed as it does not seem to make sense to have
>attributes detached from an instance.
>
>Can you make an example where this can be meaningful. And why you need
>to use attributes to do that ?

I was hoping to avoid the "why do you need to do that" sorts of questions, since they
can be distracting from the problem at hand, but OK.

The attribute I'd like to define is a "Friendly Name" attribute.  This name will be
used in, for example, error messages involving the class.  I may define a class
called, say, "EntLOB", but I want to use a more friendly name, like, "Enterprise Line
of Business" when presenting errors involving that class.

Many times the "Friendly Name" is the same as the class name (like "Customer"), but
in more than a few cases the class name I may use as a programmer is not the best
choice as a name for presentation to an end user.

It's true that almost all error messages would involve an instance of the class, but
consider the case where an instance of the object cannot be created at all.  Perhaps
the user has entered some data (e.g. search criteria) which prevents an instance from
being created.  The network could be down.  The database could be offline.  I'd like
the error message to be something like:

    Unable to retrieve the Enterprise Line of Business because ...

rather than

    Unable to retrieve the EntLOB because ...

The former is more, er, friendly, than the latter.

Since the database went offline, the object could not be constructed and there is no
instance of the EntLOB class available to get the type of. Since all the objects that
I may create derive from a common base class, I'd like to decorate my derived classes
with this attribute as appropriate, and have a common method, implemented in the base
class, which accesses this attribute and returns the specified name from the
attribute. 

Since I may need to access this property even if no specific instance of the derived
class exists, I want to define a shared method in the base class which returns the
friendly name of the derived class (i.e. rather than the name of an instance), which
is where the attribute is located.  I was hoping that I could use syntax like:

errormsg = string.format("Unable to retrieve {0} ...", EntLOB.FriendlyName, ...) ...

where FriendlyName is a shared readonly property implemented in the base class.

HTH,

  -- Jeff
Author
31 Jul 2006 12:04 PM
tommaso.gastaldi
hi Jeff,

Couldn't you, in case of error, just  create a "dummy" instance of the
derived class,
decorated as you wish?

In this case you could also change the attributes depending on the
exception type.
Do you think that could make sense?

-tom

Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 01:54:21 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >Hi Jeff,
> >
> >if the problem were just to obtain the type you could use
> >GetType(YourBaseClass)  ...
>
> I think I tried GetType(YourBaseClass), but it returned the type of the Base class
> instead of the derived class.  The derived class is where the attribute is located.
>
> >but I am a little perplexed as it does not seem to make sense to have
> >attributes detached from an instance.
> >
> >Can you make an example where this can be meaningful. And why you need
> >to use attributes to do that ?
>
> I was hoping to avoid the "why do you need to do that" sorts of questions, since they
> can be distracting from the problem at hand, but OK.
>
> The attribute I'd like to define is a "Friendly Name" attribute.  This name will be
> used in, for example, error messages involving the class.  I may define a class
> called, say, "EntLOB", but I want to use a more friendly name, like, "Enterprise Line
> of Business" when presenting errors involving that class.
>
> Many times the "Friendly Name" is the same as the class name (like "Customer"), but
> in more than a few cases the class name I may use as a programmer is not the best
> choice as a name for presentation to an end user.
>
> It's true that almost all error messages would involve an instance of the class, but
> consider the case where an instance of the object cannot be created at all.  Perhaps
> the user has entered some data (e.g. search criteria) which prevents an instance from
> being created.  The network could be down.  The database could be offline.  I'd like
> the error message to be something like:
>
>     Unable to retrieve the Enterprise Line of Business because ...
>
> rather than
>
>     Unable to retrieve the EntLOB because ...
>
> The former is more, er, friendly, than the latter.
>
> Since the database went offline, the object could not be constructed and there is no
> instance of the EntLOB class available to get the type of. Since all the objects that
> I may create derive from a common base class, I'd like to decorate my derived classes
> with this attribute as appropriate, and have a common method, implemented in the base
> class, which accesses this attribute and returns the specified name from the
> attribute.
>
> Since I may need to access this property even if no specific instance of the derived
> class exists, I want to define a shared method in the base class which returns the
> friendly name of the derived class (i.e. rather than the name of an instance), which
> is where the attribute is located.  I was hoping that I could use syntax like:
>
>  errormsg = string.format("Unable to retrieve {0} ...", EntLOB.FriendlyName, ...) ...
>
> where FriendlyName is a shared readonly property implemented in the base class.
>
> HTH,
>
>   -- Jeff
Author
31 Jul 2006 1:49 PM
Jeff Mason
On 31 Jul 2006 05:04:38 -0700, tommaso.gasta***@uniroma1.it wrote:

>hi Jeff,
>
>Couldn't you, in case of error, just  create a "dummy" instance of the
>derived class, decorated as you wish?
>
>In this case you could also change the attributes depending on the
>exception type.
>Do you think that could make sense?
>
Hi Tom,

I suppose, but error exceptions may not be the only reason why the UI would want to
display the friendly name of a class without having an instance of the class.  It
happens that a failure to retrieve was the immediate example I thought of in my
reply, but I can certainly think of other reasons (labels, captions, informational
messages, etc.)

(This is why I don't like answering the "why are you doing this" types of questions -
we can get afield of the orginal question :-)

Obviously, I can require that a derived class implement a specific (shared) friendly
name method which returns the derived class's friendly name via MustImplement
definitions in the base class.

But, I was hoping for a more "elegant" solution using a custom attribute.  That way,
the developer of the derived class can supply the custom attribute only if the
friendly name is different than the class name, and all they have to do is provide
the attribute; the base class takes care of everything else.

This was easy to do when I had an instance of the derived class.

So I guess the question boils down to how do I get the Type of a derived class from
within a SHARED method in that derived class's base class (where there is no
instance)?

Is this even possible via reflection?  (It feels like it ought to be, but what do I
know...)

  -- Jeff
Author
31 Jul 2006 2:32 PM
tommaso.gastaldi
See if this get close to what you want (watch out line breaks, VB
2005):


Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

        'Instance version
        Dim MyDerivedClass As New MyDerivedClass_Instance
        With MyDerivedClass
            MsgBox(.ValueFromMyAttribute(1))
            MsgBox(.ValueFromMyAttribute(2))
        End With

        'Shared version
        MsgBox(MyBaseClass.S_ValueFromMyAttribute(1))
        MsgBox(MyBaseClass.S_ValueFromMyAttribute(2))

    End Sub

    Class MyCustomAttribute
        Inherits Attribute

        Public PropertyForString1 As String
        Public PropertyForString2 As String

        Sub New(ByVal s1 As String, ByVal s2 As String)
            Me.PropertyForString1 = s1
            Me.PropertyForString2 = s2
        End Sub

    End Class

    <MyCustomAttribute("Default1", "Default2")> _
      Public Class MyDerivedClass
        Inherits MyBaseClass

    End Class

    <MyCustomAttribute("Hi", "Jeff")> _
  Public Class MyDerivedClass_Instance
        Inherits MyBaseClass

    End Class

    Class MyBaseClass

        Public ReadOnly Property ValueFromMyAttribute(ByVal Index As
Integer) As String
            Get
                Dim classtype As Type = Me.GetType
                Dim attr As MyCustomAttribute =
DirectCast(Attribute.GetCustomAttribute(classtype,
GetType(MyCustomAttribute)), MyCustomAttribute)
                If attr IsNot Nothing Then
                    Select Case Index
                        Case 1
                            Return attr.PropertyForString1
                        Case 2
                            Return attr.PropertyForString2
                        Case Else
                            Return "unexpected arg"
                    End Select
                Else
                    Return "no attribute"
                End If
            End Get
        End Property


        Public Shared ReadOnly Property S_ValueFromMyAttribute(ByVal
Index As Integer) As String
            Get
                Dim classtype As Type = GetType(MyDerivedClass)
                Dim attr As MyCustomAttribute =
DirectCast(Attribute.GetCustomAttribute(classtype,
GetType(MyCustomAttribute)), MyCustomAttribute)
                If attr IsNot Nothing Then
                    Select Case Index
                        Case 1
                            Return attr.PropertyForString1
                        Case 2
                            Return attr.PropertyForString2
                        Case Else
                            Return "unexpected arg"
                    End Select
                Else
                    Return "no attribute"
                End If
            End Get
        End Property

    End Class

End Class


-tom

Datatime Community (free)
http://cam70.sta.uniroma1.it/Community/


Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 05:04:38 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >hi Jeff,
> >
> >Couldn't you, in case of error, just  create a "dummy" instance of the
> >derived class, decorated as you wish?
> >
> >In this case you could also change the attributes depending on the
> >exception type.
> >Do you think that could make sense?
> >
> Hi Tom,
>
> I suppose, but error exceptions may not be the only reason why the UI would want to
> display the friendly name of a class without having an instance of the class.  It
> happens that a failure to retrieve was the immediate example I thought of in my
> reply, but I can certainly think of other reasons (labels, captions, informational
> messages, etc.)
>
> (This is why I don't like answering the "why are you doing this" types of questions -
> we can get afield of the orginal question :-)
>
> Obviously, I can require that a derived class implement a specific (shared) friendly
> name method which returns the derived class's friendly name via MustImplement
> definitions in the base class.
>
> But, I was hoping for a more "elegant" solution using a custom attribute.  That way,
> the developer of the derived class can supply the custom attribute only if the
> friendly name is different than the class name, and all they have to do is provide
> the attribute; the base class takes care of everything else.
>
> This was easy to do when I had an instance of the derived class.
>
> So I guess the question boils down to how do I get the Type of a derived class from
> within a SHARED method in that derived class's base class (where there is no
> instance)?
>
> Is this even possible via reflection?  (It feels like it ought to be, but what do I
> know...)
>
>   -- Jeff
Author
31 Jul 2006 3:11 PM
Jeff Mason
On 31 Jul 2006 07:32:01 -0700, tommaso.gasta***@uniroma1.it wrote:

>
>See if this get close to what you want (watch out line breaks, VB
>2005):

[snippage]

>  Dim MyDerivedClass As New MyDerivedClass_Instance

[snippage]

>    <MyCustomAttribute("Hi", "Jeff")> _
>  Public Class MyDerivedClass_Instance
>        Inherits MyBaseClass
>
>    End Class
>
>    Class MyBaseClass
>

[snippage]

Show quoteHide quote
>        Public Shared ReadOnly Property S_ValueFromMyAttribute(ByVal
>Index As Integer) As String
>            Get
>                Dim classtype As Type = GetType(MyDerivedClass)
>                Dim attr As MyCustomAttribute =
>DirectCast(Attribute.GetCustomAttribute(classtype,
>GetType(MyCustomAttribute)), MyCustomAttribute)
>                If attr IsNot Nothing Then
>                    Select Case Index
>                        Case 1
>                            Return attr.PropertyForString1
>                        Case 2
>                            Return attr.PropertyForString2
>                        Case Else
>                            Return "unexpected arg"
>                    End Select
>                Else
>                    Return "no attribute"
>                End If
>            End Get
>        End Property
>
>    End Class
>
>End Class
>
Ah, but MyBaseClass is used as a base for many different classes, so referring to the
derived class via GetType(MyDerivedClass) won't do, since that "hardwires" a
particular derived class inside the base class. Any number of other classes would
also derive from the base, each with their own attribute.

As I think about it, there could even be an entire class hierarchy which derives from
the base class.  It would be a reasonable restriction for the base to assume that the
attribute only appeared on the most derived class from the base.

  -- Jeff
Author
31 Jul 2006 3:32 PM
tommaso.gastaldi
I anticipated your observation by 2 using 2 classes:

MyDerivedClass_Instance
MyDerivedClass

both display correct attributes, although the same type is used in the
gettype.
Can you make an example where this scheme would fail, so that we can
fix it ?

-tom

Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 07:32:01 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >
> >See if this get close to what you want (watch out line breaks, VB
> >2005):
>
> [snippage]
>
> >  Dim MyDerivedClass As New MyDerivedClass_Instance
>
> [snippage]
>
> >    <MyCustomAttribute("Hi", "Jeff")> _
> >  Public Class MyDerivedClass_Instance
> >        Inherits MyBaseClass
> >
> >    End Class
> >
> >    Class MyBaseClass
> >
>
> [snippage]
>
> >        Public Shared ReadOnly Property S_ValueFromMyAttribute(ByVal
> >Index As Integer) As String
> >            Get
> >                Dim classtype As Type = GetType(MyDerivedClass)
> >                Dim attr As MyCustomAttribute =
> >DirectCast(Attribute.GetCustomAttribute(classtype,
> >GetType(MyCustomAttribute)), MyCustomAttribute)
> >                If attr IsNot Nothing Then
> >                    Select Case Index
> >                        Case 1
> >                            Return attr.PropertyForString1
> >                        Case 2
> >                            Return attr.PropertyForString2
> >                        Case Else
> >                            Return "unexpected arg"
> >                    End Select
> >                Else
> >                    Return "no attribute"
> >                End If
> >            End Get
> >        End Property
> >
> >    End Class
> >
> >End Class
> >
> Ah, but MyBaseClass is used as a base for many different classes, so referring to the
> derived class via GetType(MyDerivedClass) won't do, since that "hardwires" a
> particular derived class inside the base class. Any number of other classes would
> also derive from the base, each with their own attribute.
>
> As I think about it, there could even be an entire class hierarchy which derives from
> the base class.  It would be a reasonable restriction for the base to assume that the
> attribute only appeared on the most derived class from the base.
>
>   -- Jeff
Author
31 Jul 2006 4:14 PM
Jeff Mason
On 31 Jul 2006 08:32:37 -0700, tommaso.gasta***@uniroma1.it wrote:

>I anticipated your observation by 2 using 2 classes:
>
>MyDerivedClass_Instance
>MyDerivedClass
>
>both display correct attributes, although the same type is used in the
>gettype.
>Can you make an example where this scheme would fail, so that we can
>fix it ?
>
I really appreciate your help and interest here.

Let's concentrate on the issue where no class instance is involved, and the shared
method on the base class is invoked through the derived class (since that's were my
problem lies).

Suppose I define two classes both decorated with their own version of the attribute:

    <MyCustomAttribute("Default1", "Default2")> _
      Public Class MyDerivedClass
        Inherits MyBaseClass

    End Class

    <MyCustomAttribute("Hi", "Tom")> _
      Public Class AnotherDerivedClass
        Inherits MyBaseClass

    End Class

How would the assignment of the 'classtype' variable in the base's shared method be
made?  As you originally suggested, it was:

    Dim classtype As Type = GetType(MyDerivedClass)

but now I have two classes, MyDerivedClass AND AnotherDerivedClass, both of which
derive from MyBaseClass, and for both of which I'd like to evaluate each's attribute,
as, say:

   Dim x as string = MyDerivedClass.S_ValueFromMyAttribute(1)
   Dim y as string = AnotherDerivedClass.S_ValueFromMyAttribute(1)

I would hope to return x = "Default1" and y = "Hi".

It seems to me that GetType won't work here as it needs an explicit Type as its
argument.  Since there can be multiple classes that inherit from the base, I don't
know that Type in the base class, as it is only known at run-time when the shared
method is invoked.

I'm thinking that some other form of reflection is required to get the Type of the
derived classin the base's shared method.  I have no idea what that might be...

  -- Jeff
Author
31 Jul 2006 5:19 PM
tommaso.gastaldi
Ok let's make another attempt...  :)

What do you think of this ?  (at least the result is that wanted)
It only requires an additional statement in the derived class:


Imports System.Reflection

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

        'Shared version

        MsgBox(AnotherDerivedClass.ValueFromMyAttribute_Static(1,
AnotherDerivedClass.WhoAmI))
        MsgBox(AnotherDerivedClass.ValueFromMyAttribute_Static(2,
AnotherDerivedClass.WhoAmI))

        MsgBox(MyDerivedClass.ValueFromMyAttribute_Static(1,
MyDerivedClass.WhoAmI))
        MsgBox(MyDerivedClass.ValueFromMyAttribute_Static(2,
MyDerivedClass.WhoAmI))

    End Sub

    Class MyCustomAttribute
        Inherits Attribute

        Public PropertyForString1 As String
        Public PropertyForString2 As String

        Sub New(ByVal s1 As String, ByVal s2 As String)
            Me.PropertyForString1 = s1
            Me.PropertyForString2 = s2
        End Sub

    End Class

    <MyCustomAttribute("Default1", "Default2")> _
       Public Class MyDerivedClass
        Inherits MyBaseClass

        Public Shared WhoAmI As Type = GetType(MyDerivedClass)

    End Class

    <MyCustomAttribute("Hi", "Jeff")> _
      Public Class AnotherDerivedClass
        Inherits MyBaseClass

        Public Shared WhoAmI As Type = GetType(AnotherDerivedClass)

    End Class


    Class MyBaseClass

        Public Shared ReadOnly Property
ValueFromMyAttribute_Static(ByVal Index As Integer, ByVal WhoAmI As
Type) As String
            Get
                Dim classtype As Type = WhoAmI
                Dim attr As MyCustomAttribute =
DirectCast(Attribute.GetCustomAttribute(classtype,
GetType(MyCustomAttribute)), MyCustomAttribute)
                If attr IsNot Nothing Then
                    Select Case Index
                        Case 1
                            Return attr.PropertyForString1
                        Case 2
                            Return attr.PropertyForString2
                        Case Else
                            Return "unexpected arg"
                    End Select
                Else
                    Return "no attribute"
                End If
            End Get
        End Property

    End Class


-Tom




Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 08:32:37 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >I anticipated your observation by 2 using 2 classes:
> >
> >MyDerivedClass_Instance
> >MyDerivedClass
> >
> >both display correct attributes, although the same type is used in the
> >gettype.
> >Can you make an example where this scheme would fail, so that we can
> >fix it ?
> >
> I really appreciate your help and interest here.
>
> Let's concentrate on the issue where no class instance is involved, and the shared
> method on the base class is invoked through the derived class (since that's were my
> problem lies).
>
> Suppose I define two classes both decorated with their own version of the attribute:
>
>     <MyCustomAttribute("Default1", "Default2")> _
>       Public Class MyDerivedClass
>         Inherits MyBaseClass
>
>     End Class
>
>     <MyCustomAttribute("Hi", "Tom")> _
>       Public Class AnotherDerivedClass
>         Inherits MyBaseClass
>
>     End Class
>
> How would the assignment of the 'classtype' variable in the base's shared method be
> made?  As you originally suggested, it was:
>
>     Dim classtype As Type = GetType(MyDerivedClass)
>
> but now I have two classes, MyDerivedClass AND AnotherDerivedClass, both of which
> derive from MyBaseClass, and for both of which I'd like to evaluate each's attribute,
> as, say:
>
>    Dim x as string = MyDerivedClass.S_ValueFromMyAttribute(1)
>    Dim y as string = AnotherDerivedClass.S_ValueFromMyAttribute(1)
>
> I would hope to return x = "Default1" and y = "Hi".
>
> It seems to me that GetType won't work here as it needs an explicit Type as its
> argument.  Since there can be multiple classes that inherit from the base, I don't
> know that Type in the base class, as it is only known at run-time when the shared
> method is invoked.
>
> I'm thinking that some other form of reflection is required to get the Type of the
> derived classin the base's shared method.  I have no idea what that might be...
>
>   -- Jeff
Author
31 Jul 2006 6:49 PM
Jeff Mason
On 31 Jul 2006 10:19:57 -0700, tommaso.gasta***@uniroma1.it wrote:

>Ok let's make another attempt...  :)
>
>What do you think of this ?  (at least the result is that wanted)
>It only requires an additional statement in the derived class:

Well, that does indeed give the result wanted.

Please don't take this the wrong way, because I really do appreciate your help, but
it does make me think that it's a bit of hackery. (which is not necessarily a
prejorative term :-)

I guess the problem that I have with it is that it requires the developer of the
derived class to deal with the friendly name issue on three separate fronts.  He must
provide the attribute on the class definition.  He must define a constant within a
class that is the type of the very class he is defining.  He must provide this
constant in every call to the FriendlyName method.

As I alluded to in an earlier post, I have in fact dealt with this issue in the past
by defining a FriendlyName (shared) method in the derived class.  The base class
defined a MustInherit FriendlyName method, and each derived class in turn implemented
the method and returned the appropriate string.

I noticed, however, that the majority of the classes we defined had their
FriendlyName the same as their class name (e.g.'Customer'), so this resulted in a lot
of superfluous methods.  I then got this bright idea to use an attribute instead. The
idea is that the developer would include the attribute only if a friendly name was
different than the class name. It seems to me that it is much simpler to define the
attribute when needed, and then reference the shared method "through" the derived
class.  If no attribute was present, then the FriendlyName method would simply return
the ClassName.

Unfortunately I'm unable to find the magic incantation using reflection to do this.
Clearly, the runtime is in fact aware that MyDerivedClass inherits from MyBaseClass.
This is because MyDerivedClass.FriendlyName in fact invokes the FriendlyName method
in MyBaseClass. I was hoping that given that, there was a way to reflect "back" from
the base class to the derived class that invoked the method in it, without having an
instance of the derived class available to get the derived type.

sigh.


  -- Jeff
Author
31 Jul 2006 7:52 PM
tommaso.gastaldi
Yes Jeff, I know it's a little bit hackery :) But it gets the job done
in a way that is, somehow, general. If I find a more elegant way I will
tell you, but I doubt it can be done with "pure" reflection, which is
based on instances. Actually, I was trying to be of help in an attempt
to convince myself that attributes are really useful for something.
But, at the present, I can see ways to do the same things in a much
more clear and maintenable way and I am not really persuaded of the
real usefulness of this stuff. For instance, often, enumerations used
in conjuction with hashtables and classes of attributes can get the job
done in a much more clear and maintenable way. I hope in the future to
see some useful way to integrate this notion in my programs, but so
far, frankly speaking, I am unable to give to this attribute stuff an
overwhelming importance. If you see situations where attributes are
really a useful replacement of other technique I would be very
interested to know them :)

-Tom

Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 10:19:57 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >Ok let's make another attempt...  :)
> >
> >What do you think of this ?  (at least the result is that wanted)
> >It only requires an additional statement in the derived class:
>
> Well, that does indeed give the result wanted.
>
> Please don't take this the wrong way, because I really do appreciate your help, but
> it does make me think that it's a bit of hackery. (which is not necessarily a
> prejorative term :-)
>
> I guess the problem that I have with it is that it requires the developer of the
> derived class to deal with the friendly name issue on three separate fronts.  He must
> provide the attribute on the class definition.  He must define a constant within a
> class that is the type of the very class he is defining.  He must provide this
> constant in every call to the FriendlyName method.
>
> As I alluded to in an earlier post, I have in fact dealt with this issue in the past
> by defining a FriendlyName (shared) method in the derived class.  The base class
> defined a MustInherit FriendlyName method, and each derived class in turn implemented
> the method and returned the appropriate string.
>
> I noticed, however, that the majority of the classes we defined had their
> FriendlyName the same as their class name (e.g.'Customer'), so this resulted in a lot
> of superfluous methods.  I then got this bright idea to use an attribute instead. The
> idea is that the developer would include the attribute only if a friendly name was
> different than the class name. It seems to me that it is much simpler to define the
> attribute when needed, and then reference the shared method "through" the derived
> class.  If no attribute was present, then the FriendlyName method would simply return
> the ClassName.
>
> Unfortunately I'm unable to find the magic incantation using reflection to do this.
> Clearly, the runtime is in fact aware that MyDerivedClass inherits from MyBaseClass.
> This is because MyDerivedClass.FriendlyName in fact invokes the FriendlyName method
> in MyBaseClass. I was hoping that given that, there was a way to reflect "back" from
> the base class to the derived class that invoked the method in it, without having an
> instance of the derived class available to get the derived type.
>
> sigh.
>
>
>   -- Jeff
Author
31 Jul 2006 8:51 PM
Jeff Mason
On 31 Jul 2006 12:52:37 -0700, tommaso.gasta***@uniroma1.it wrote:

Show quoteHide quote
>Yes Jeff, I know it's a little bit hackery :) But it gets the job done
>in a way that is, somehow, general. If I find a more elegant way I will
>tell you, but I doubt it can be done with "pure" reflection, which is
>based on instances. Actually, I was trying to be of help in an attempt
>to convince myself that attributes are really useful for something.
>But, at the present, I can see ways to do the same things in a much
>more clear and maintenable way and I am not really persuaded of the
>real usefulness of this stuff. For instance, often, enumerations used
>in conjuction with hashtables and classes of attributes can get the job
>done in a much more clear and maintenable way. I hope in the future to
>see some useful way to integrate this notion in my programs, but so
>far, frankly speaking, I am unable to give to this attribute stuff an
>overwhelming importance. If you see situations where attributes are
>really a useful replacement of other technique I would be very
>interested to know them :)
>
>-Tom

Tom,

We found the following class (watch wrapping) which uses attributes useful.  We use
enumerations all over the place, and it is helpful sometimes to define a "friendly"
description associated with an enum value.  Of course, the ToString method on an
enumeration value will give you the name as defined by the enumeration, but we've
found it helpful to be able to attach an alternate Friendly Name to some enumerations
(to load up a combo box, say).

This uses the DescriptionAttribute located in the System.ComponentModel namespace
(since that was handy), but the technique could be applied to a custom attribute if
you're so inclined.

Thanks for your help, and it looks like I'll be abandoning the idea of a custom
attribute because unfortunately my requirement is that instances may not exist.

-----
Imports System.Reflection
Imports System.ComponentModel

Public Class EnumHelper

   'Given an enumeration value, return its description attribute of that value.
   'e.g. given:
   '
   '  Public Enum people
   '     <Description("Jeff Mason")> Jeff
   '     <Description("Dirk Digler")> Dirk
   '     <Description("John Smith")> John
   '     <Description("Sam Spade")> Sam
   '  End Enum
   '
   'then calling this:
   '
   '  GetEnumDescription(people.Jeff)
   '
   'would return this:
   '
   '  "Jeff Mason"
   '
   Public Shared Function GetEnumDescription(ByVal value As [Enum]) As String

      Dim fi As FieldInfo = value.GetType().GetField(value.ToString)
      Dim attribs() As DescriptionAttribute =
DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False),
DescriptionAttribute())

      If attribs.Length > 0 Then
         Return attribs(0).Description
      Else
         Return value.ToString
      End If

   End Function

   'Given an enumeration and the description of one of the items in it,
   'get the value of the corresponding enumeration item.
   '
   'Given the people enum as above, calling:
   '
   '  GetEnumValue(GetType(people), "John Smith")
   '
   'would return:
   '
   '  2
   '
   'Note that this function is optimized for integer enums.  Use GetEnumValueObj if
the
   'enum is some other type.
   Public Shared Function GetEnumValue(ByVal t As Type, ByVal description As String)
As Integer

      For Each e As [Enum] In e.GetValues(t)
         If GetEnumDescription(e) = description Then
            'its this one, so get the value from it
            Dim i As FieldInfo = e.GetType().GetField(e.ToString)
            Return CInt(i.GetValue(e))
         End If
      Next

      Return Nothing

   End Function

   Public Shared Function GetEnumValueObj(ByVal t As Type, ByVal description As
String) As Object

      For Each e As [Enum] In e.GetValues(t)
         If GetEnumDescription(e) = description Then
            'its this one, so get the value from it
            Dim i As FieldInfo = e.GetType().GetField(e.ToString)
            Return i.GetValue(e)
         End If
      Next

      Return Nothing

   End Function

   'Given an enumeration and a description, check if any items in the enumeration
   'have that description.
   '
   'Given the people enum as above, calling:
   '
   '  GetEnumValueExists(GetType(people), "John Smith")
   '
   'would return true
   '
   'On the other hand calling:
   '
   '  GetEnumValueExists(GetType(people), "Bob Jones")
   '
   'would return false
   '
   Public Shared Function GetEnumValueExists(ByVal t As Type, ByVal description As
String) As Boolean

      For Each e As [Enum] In e.GetValues(t)
         If GetEnumDescription(e) = description Then
            'there is a match
            Return True
         End If
      Next
      'there isn't
      Return False

   End Function

   'Get an arraylist of descriptions from an enumeration.
   '(useful for populating comboboxes, etc.)
   Public Shared Function GetEnumDescriptionArrayList(ByVal t As Type) As ArrayList

      Dim al As New ArrayList
      For Each e As [Enum] In e.GetValues(t)
         al.Add(GetEnumDescription(e))
      Next

      Return al

   End Function

End Class

  -- Jeff
Author
31 Jul 2006 9:59 PM
tommaso.gastaldi
Thank you Jeff for the remarkable example. You could actually
write an interesting article on that.

I am beginning to see some light after the "fog" :)

-tom

Jeff Mason ha scritto:

Show quoteHide quote
> On 31 Jul 2006 12:52:37 -0700, tommaso.gasta***@uniroma1.it wrote:
>
> >Yes Jeff, I know it's a little bit hackery :) But it gets the job done
> >in a way that is, somehow, general. If I find a more elegant way I will
> >tell you, but I doubt it can be done with "pure" reflection, which is
> >based on instances. Actually, I was trying to be of help in an attempt
> >to convince myself that attributes are really useful for something.
> >But, at the present, I can see ways to do the same things in a much
> >more clear and maintenable way and I am not really persuaded of the
> >real usefulness of this stuff. For instance, often, enumerations used
> >in conjuction with hashtables and classes of attributes can get the job
> >done in a much more clear and maintenable way. I hope in the future to
> >see some useful way to integrate this notion in my programs, but so
> >far, frankly speaking, I am unable to give to this attribute stuff an
> >overwhelming importance. If you see situations where attributes are
> >really a useful replacement of other technique I would be very
> >interested to know them :)
> >
> >-Tom
>
> Tom,
>
> We found the following class (watch wrapping) which uses attributes useful.  We use
> enumerations all over the place, and it is helpful sometimes to define a "friendly"
> description associated with an enum value.  Of course, the ToString method on an
> enumeration value will give you the name as defined by the enumeration, but we've
> found it helpful to be able to attach an alternate Friendly Name to some enumerations
> (to load up a combo box, say).
>
> This uses the DescriptionAttribute located in the System.ComponentModel namespace
> (since that was handy), but the technique could be applied to a custom attribute if
> you're so inclined.
>
> Thanks for your help, and it looks like I'll be abandoning the idea of a custom
> attribute because unfortunately my requirement is that instances may not exist.
>
> -----
> Imports System.Reflection
> Imports System.ComponentModel
>
> Public Class EnumHelper
>
>    'Given an enumeration value, return its description attribute of that value.
>    'e.g. given:
>    '
>    '  Public Enum people
>    '     <Description("Jeff Mason")> Jeff
>    '     <Description("Dirk Digler")> Dirk
>    '     <Description("John Smith")> John
>    '     <Description("Sam Spade")> Sam
>    '  End Enum
>    '
>    'then calling this:
>    '
>    '  GetEnumDescription(people.Jeff)
>    '
>    'would return this:
>    '
>    '  "Jeff Mason"
>    '
>    Public Shared Function GetEnumDescription(ByVal value As [Enum]) As String
>
>       Dim fi As FieldInfo = value.GetType().GetField(value.ToString)
>       Dim attribs() As DescriptionAttribute =
> DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False),
> DescriptionAttribute())
>
>       If attribs.Length > 0 Then
>          Return attribs(0).Description
>       Else
>          Return value.ToString
>       End If
>
>    End Function
>
>    'Given an enumeration and the description of one of the items in it,
>    'get the value of the corresponding enumeration item.
>    '
>    'Given the people enum as above, calling:
>    '
>    '  GetEnumValue(GetType(people), "John Smith")
>    '
>    'would return:
>    '
>    '  2
>    '
>    'Note that this function is optimized for integer enums.  Use GetEnumValueObj if
> the
>    'enum is some other type.
>    Public Shared Function GetEnumValue(ByVal t As Type, ByVal description As String)
> As Integer
>
>       For Each e As [Enum] In e.GetValues(t)
>          If GetEnumDescription(e) = description Then
>             'its this one, so get the value from it
>             Dim i As FieldInfo = e.GetType().GetField(e.ToString)
>             Return CInt(i.GetValue(e))
>          End If
>       Next
>
>       Return Nothing
>
>    End Function
>
>    Public Shared Function GetEnumValueObj(ByVal t As Type, ByVal description As
> String) As Object
>
>       For Each e As [Enum] In e.GetValues(t)
>          If GetEnumDescription(e) = description Then
>             'its this one, so get the value from it
>             Dim i As FieldInfo = e.GetType().GetField(e.ToString)
>             Return i.GetValue(e)
>          End If
>       Next
>
>       Return Nothing
>
>    End Function
>
>    'Given an enumeration and a description, check if any items in the enumeration
>    'have that description.
>    '
>    'Given the people enum as above, calling:
>    '
>    '  GetEnumValueExists(GetType(people), "John Smith")
>    '
>    'would return true
>    '
>    'On the other hand calling:
>    '
>    '  GetEnumValueExists(GetType(people), "Bob Jones")
>    '
>    'would return false
>    '
>    Public Shared Function GetEnumValueExists(ByVal t As Type, ByVal description As
> String) As Boolean
>
>       For Each e As [Enum] In e.GetValues(t)
>          If GetEnumDescription(e) = description Then
>             'there is a match
>             Return True
>          End If
>       Next
>       'there isn't
>       Return False
>
>    End Function
>
>    'Get an arraylist of descriptions from an enumeration.
>    '(useful for populating comboboxes, etc.)
>    Public Shared Function GetEnumDescriptionArrayList(ByVal t As Type) As ArrayList
>
>       Dim al As New ArrayList
>       For Each e As [Enum] In e.GetValues(t)
>          al.Add(GetEnumDescription(e))
>       Next
>
>       Return al
>
>    End Function
>
> End Class
>
>   -- Jeff
Author
1 Aug 2006 3:52 PM
Jeff Mason
Hi Tom,

I just wanted to followup on our conversation.  I think I found an
acceptable (just barely) way of handling my problem with accessing the
FriendlyName attribute from a base class when no instance of the derived
class exists.

What I did was to expose two overloaded methods in the base class, one
shared and one not.  Both of these are essentially the code I've posted
before.  The instance version of the FriendlyName property takes no arguments
and obtains the Type of the derived class via Me.GetType.  The magic of
polymorphism insures this refers to the derived class.  The call for an
instance of a derived object, which most calls are, is:

   dim name as string = MyDerivedClassInstance.FriendlyName()

The shared method requires the Type of the calling object as an argument, as
e.g.:

   dim name as string = MyDerivedClass.FriendlyName(GetType(MyDerivedClass))

Passing the Type of the "calling" class allows the shared method in the base
class to use relection to obtain the attribute if there is one and process it
accordingly.

I object only mildly to the ugliness of the GetType as an argument, and this
has the distinct advantage of actually working.

--Jeff
Author
1 Aug 2006 4:37 PM
tommaso.gastaldi
Very good, Jeff.

Probably there is no way to escape the need of passing info about
the type, because, as you rightly noted, reflection works with actual
instances (unless we are missing something). And your way is an elegant
way to do it.

Why, sometime, don't you organize this information on a web page. I
think it would
be of interest to many people.  :)

-Tom

Jeff Mason (Nospam) ha scritto:

Show quoteHide quote
> Hi Tom,
>
> I just wanted to followup on our conversation.  I think I found an
> acceptable (just barely) way of handling my problem with accessing the
> FriendlyName attribute from a base class when no instance of the derived
> class exists.
>
> What I did was to expose two overloaded methods in the base class, one
> shared and one not.  Both of these are essentially the code I've posted
> before.  The instance version of the FriendlyName property takes no arguments
> and obtains the Type of the derived class via Me.GetType.  The magic of
> polymorphism insures this refers to the derived class.  The call for an
> instance of a derived object, which most calls are, is:
>
>    dim name as string = MyDerivedClassInstance.FriendlyName()
>
> The shared method requires the Type of the calling object as an argument, as
> e.g.:
>
>    dim name as string = MyDerivedClass.FriendlyName(GetType(MyDerivedClass))
>
> Passing the Type of the "calling" class allows the shared method in the base
> class to use relection to obtain the attribute if there is one and process it
> accordingly.
>
> I object only mildly to the ugliness of the GetType as an argument, and this
> has the distinct advantage of actually working.

>  --Jeff