|
web
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Custom Attributes, Shared methods, Derived classes and reflectionI'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 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 On 31 Jul 2006 01:54:21 -0700, tommaso.gasta***@uniroma1.it wrote:
>Hi Jeff, I think I tried GetType(YourBaseClass), but it returned the type of the Base class> >if the problem were just to obtain the type you could use >GetType(YourBaseClass) ... 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 I was hoping to avoid the "why do you need to do that" sorts of questions, since they>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 ? 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 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 On 31 Jul 2006 05:04:38 -0700, tommaso.gasta***@uniroma1.it wrote:
>hi Jeff, I suppose, but error exceptions may not be the only reason why the UI would want to> >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, 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 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 On 31 Jul 2006 07:32:01 -0700, tommaso.gasta***@uniroma1.it wrote:
> [snippage]>See if this get close to what you want (watch out line breaks, VB >2005): > Dim MyDerivedClass As New MyDerivedClass_Instance [snippage]> <MyCustomAttribute("Hi", "Jeff")> _ [snippage]> Public Class MyDerivedClass_Instance > Inherits MyBaseClass > > End Class > > Class MyBaseClass > Show quoteHide quote > Public Shared ReadOnly Property S_ValueFromMyAttribute(ByVal Ah, but MyBaseClass is used as a base for many different classes, so referring to the>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 > 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 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 On 31 Jul 2006 08:32:37 -0700, tommaso.gasta***@uniroma1.it wrote:
>I anticipated your observation by 2 using 2 classes: I really appreciate your help and interest here.> >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 ? > 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 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 On 31 Jul 2006 10:19:57 -0700, tommaso.gasta***@uniroma1.it wrote:
>Ok let's make another attempt... :) Well, that does indeed give the result wanted.> >What do you think of this ? (at least the result is that wanted) >It only requires an additional statement in the derived class: 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 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 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 Tom,>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 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 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 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 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
Check result of call into Windows API
allowing just numeric value in my textbox *** HELP *** Problems Accessing Simple VB.NET Access Database Multi-Threaded App Cross-thread operation not valid How to validate Date entries. Regex doesn't match when test string is in middle of file Wanted: Simple VB.NET "WEB" Application with an Access Database String translation Saving an image from the browser |
|||||||||||||||||||||||