|
I never saw the need to create new attributes until one day I stumbled into a problem:
How can I validate and normalize the fields of my business objects before I try to insert them in the database? I scratched my head over the idea for a few days on how to create reusable validation for different fields in an easy and intuitive way.
Attributes came to mind with a big: "AHA!"
So what I did was create Normalization and Validation attributes. I create two abstract attribute classes (normalization and validation) and all the other attributes inherited from these two.
Then I created an abstract class as a base class for all my business objects. This abstract classes implemented Validate and Normalize methods. Both these methods performed reflection to read attributes of the derived class and as such I had to implement it only once for all classes.
Now with those prepared I could start marking my fields with the attributes. Some examples:
1 - FieldName: This marked a field with a name to be displayed to the user when validation fails in a way to display the user where the problem is.
1 - MaxStringLength: This validation attribute marked a field of the class with a max string length, so it would match the database. With that attribute I could do two things:
a) Ensure string does not get truncated to the database and inform the user of the problem
b) Set the maxLength of textBoxes once in a centralized way. If database change, I only had to change it in one place (the attribute value on the field of the class) and it would automatically reflect on the textboxes related to that field.
2 - UpperCase: This normalization atribute automatically converted all marked fields to uppercase
3 - Numeric: This ensured that legacy varchar fields on the database that should contain numbers only stayed that way.
4 - Fill: This normalization could fill a field that is marked with maxStringLength attribute with the desired character up to its maximum length in a desired direction. Example: Fill with zeroes to the left.
These are just a few examples on how attributes made my life so much easier and very intuitive. I could create a business object and all I had to do was to mark the fields with these very intuitive attributes and saved me a lot of coding. And I mean A VERY BIG LOT.
Because of that, the business rules on the classes could be shrank dramatically. And this is only the beginning. There's lot more you can do with them.
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
|
following the proverb
First impression the best impression.
So I used to put attributes in my first week of coding. To impress my leadres
My Mind is the Devil's Workshop.
|
|
|
|
|
I do use them, and I think it's cool, but... I'm not sure if they quite fit anyway... I guess maybe I'm over analyzing it, but they're like some weird interdimensional communication between design time and runtime -- like configuration is supposed to be, except that you have to recompile it to affect any change. So they're like zombies. Neither fully runtime nor compile time.
|
|
|
|
|
Once you get to understand them, you'll realize they are fully runtime and are used through reflection. milkplus provided a few useful links where you can try to understand them better (I encourage you to do so) and as he said, at first it looks like magic, but it's not really.
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
I'm afraid you have completely misunderstood me. I know they're applied at runtime by reflection, but you still have to SET them at design time. This means they are neither. Configuration, as in, a file I can change without recompiling my code, is what normally serves this purpose. The attributes which have all of the baggage of implementation as do configuration elements have one less benefit: they must be modified before compilation. I think that they're a paradigm breaking thing. That's what I meant by they're odd. I think templated metaprogramming, which actually requires compilation, could be triggered by attributes in an ideal language/enviornmnet, but it's not, they're just configuration you have to set at design time.
|
|
|
|
|
Sorry if I misread you and, yeah, they are odd. But then, they behave the same way as regular code implementation. You code them, you build them, you run them.
Not very different. I don't think they should be treated as a configuration feature. They are implementation, actual business logic that is abstracted in a different way and in this case also accessed in a different way (reflection)
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
The attributes are used to settle branching logic (if this attribute present, then do this,) usually, or as parameters to what gets instantiated. Instead of having the branching logic, it would be better just to have the right object created with the right logic hard-coded as it's behavior. Which object? Use configuration to decide. I see how for HUMANS the attributes make things easier. But not really any easier than say, generics, or in c++ templates, which actually do something before runtime: they change the code to match, instead of just make a mark for your branching logic to pick up. I guess it's not a paradigm breaking thing for the USER, but it is for the implementer of attributes themselves and logic based on consuming them with reflection.
|
|
|
|
|
dave.dolan wrote: it would be better just to have the right object created with the right logic hard-coded as it's behavior.
They are exactly that, an object with a hardcoded behaviour.
dave.dolan wrote: Use configuration to decide.
Why add more complexity to it? This makes it much harder to read and to maintain. Config files can't be debugged and have breakpoints, they break the line of thinking of a human, making him look somewhere else.
dave.dolan wrote: I guess it's not a paradigm breaking thing for the USER, but it is for the implementer of attributes themselves and logic based on consuming them with reflection.
It's not that big of a deal. As an implementer, they made my life so much easier and it is not all that paradigm breaker. I plan to write an article about custom attributes and how simple they can be to implement. Also, read my post on this poll bout how they made my life easier:
It's awesome when you figure its power
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
I have used and implemented them before. I've been doing .net since 2002. I'm just saying they're strange from a language design perspective. I'm not dissing them. I'm not dissing you. I just think they are an odd design decision on the part of the original language creators. I know they come from java, and that's fine. I know they have their uses. While I did say "hardcoded" I should have said "single pathed" because the presence of one or more or many or no attribute is an open ended scenario. I didn't meant it was somehow dynamic, and I don't have a lack of understanding for what they are. I still don't agree that they fit, from a design of the language(s) perspective.
|
|
|
|
|
I think I get what you're trying to say. I guess you have a point about not fitting in a traditional OOP perspective. In some ways they resemble a dynamic language but there's where we get philosophical about the matter.
When that happens, its about everyone's opinion. Are they worth it? I'd say definitely yes. Some might say no. But I'm glad I have the option.
In the other hand, the introduction of the dynamic keyword on framework 4 to me it is not worth it. It creates a range of bad practices that might end up on your lap someday when you inherit a project. Attributes on the other hand are more obscure to beginners and as such have less probability to have bad and mysterious code along.
.Net is evolving and it's setting ground on foreign lands. I think it's a good thing as of now, as long it doesn't stretch too much out of its purpose. There's always going to be things that are fit for some and ugly for others. I can just hope that .net does not become an over crumbled platform.
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
Definitely agree about the dynamic keyword... but I'll just opt out of using it, at least until someone changes my mind about that. Maybe they will.
|
|
|
|
|
Yep, me too. But they can be useful when doing some COM interop though.
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
dave.dolan wrote: like configuration is supposed to be
So write Attributes that read the configuration.
(Darn, now I have to contrive an example... )
[Later] And so I wrote...
using Prop=System.Collections.Generic.KeyValuePair<System.Reflection.PropertyInfo,object> ;
using Proptionary=System.Collections.Generic.Dictionary<System.Reflection.PropertyInfo,object> ;
public class ConfigValueAttribute : System.ComponentModel.DefaultValueAttribute
{
private static readonly System.Collections.Generic.Dictionary<string,System.Xml.XmlDocument> doc ;
private static readonly System.Collections.Generic.Dictionary<System.Type,Proptionary> typ ;
static ConfigValueAttribute
(
)
{
doc = new System.Collections.Generic.Dictionary<string,System.Xml.XmlDocument>() ;
typ = new System.Collections.Generic.Dictionary<System.Type,Proptionary>() ;
return ;
}
public ConfigValueAttribute
(
string ConfigFilePath
,
string XPath
)
: base
(
null
)
{
string fil = System.Environment.ExpandEnvironmentVariables ( ConfigFilePath ) ;
if ( !doc.ContainsKey ( fil ) )
{
System.Xml.XmlDocument temp = new System.Xml.XmlDocument() ;
temp.Load ( fil ) ;
doc [ fil ] = temp ;
}
System.Xml.XmlNode nod = doc [ fil ].SelectSingleNode ( XPath ) ;
if ( nod is System.Xml.XmlAttribute )
{
this.SetValue ( nod.Value ) ;
}
else
{
this.SetValue ( nod.InnerText ) ;
}
return ;
}
public static T
Apply<T>
(
T Source
)
{
System.Type t = typeof(T) ;
if ( !typ.ContainsKey ( t ) )
{
Proptionary props = new Proptionary() ;
foreach ( System.Reflection.PropertyInfo pi in t.GetProperties() )
{
foreach ( ConfigValueAttribute att in pi.GetCustomAttributes ( typeof(ConfigValueAttribute) , false ) )
{
props [ pi ] = att.Value ;
}
}
typ [ t ] = props ;
}
foreach ( Prop prop in typ [ t ] )
{
try
{
prop.Key.SetValue
(
Source
,
System.Convert.ChangeType
(
prop.Value
,
prop.Key.PropertyType
)
,
null
) ;
}
catch
{
/* Leave it at the default */
}
}
return ( Source ) ;
}
}
public class SomeClass
{
[ConfigValueAttribute("ConfigValueAttributeTest.xml","/ConfigValueAttributeTest/StringValue")]
public string StringValue { get ; private set ; }
[ConfigValueAttribute("ConfigValueAttributeTest.xml","/ConfigValueAttributeTest/StringValue/@AttValue")]
public string AttValue { get ; private set ; }
[ConfigValueAttribute("ConfigValueAttributeTest.xml","/ConfigValueAttributeTest/NumValue")]
public int IntValue { get ; private set ; }
[ConfigValueAttribute("ConfigValueAttributeTest.xml","/ConfigValueAttributeTest/NumValue")]
public double DoubleValue { get ; private set ; }
public SomeClass
(
)
{
ConfigValueAttribute.Apply ( this ) ;
return ;
}
}
SomeClass x = new SomeClass() ;
System.Console.WriteLine ( "StringValue = {0}" , x.StringValue ) ;
System.Console.WriteLine ( "AttValue = {0}" , x.AttValue ) ;
System.Console.WriteLine ( "IntValue = {0}" , x.IntValue ) ;
System.Console.WriteLine ( "DoubleValue = {0}" , x.DoubleValue ) ;
<ConfigValueAttributeTest>
<StringValue AttValue='ATAT' >Test</StringValue>
<NumValue>3.14</NumValue>
</ConfigValueAttributeTest>
modified 22-Oct-11 10:27am.
|
|
|
|
|
|
Yep, on my first contact with them I thought: "How does this magic happen?". But then when you figure the fun about the way they work.
"To alcohol! The cause of, and solution to, all of life's problems" - Homer Simpson
|
|
|
|
|
Most of my custom Attributes are for use with enumerations.
I don't use many of the built-in Attributes because I don't write the kinds of applications that they're designed for.
modified 18-Oct-11 8:57am.
|
|
|
|
|
I second it!, however i used in-built attributes only, never made one or you can say it's too complicated for VC++ programmer like me
"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David Crow Never mind - my own stupidity is the source of every "problem" - Mixture
cheers,
Alok Gupta
VC Forum Q&A :- I/ IV
Support CRY- Child Relief and You
|
|
|
|
|
ok so having never used them before I had to solve my curiosity... unfortunately MSDN wasn't very helpful. so aside from the examples that MSDN gave can anyone else site some real world example where Attributes would be used??
|
|
|
|
|
A webservice is implemented as a class like any other. It can have many methods, but not all should be exposed as webmethods of the service. That's why you decorate the class itself with the 'webservice' attribute to specify that this class will act as a webservice and exposes webmethods. And only those methods wich are supposed to become webmethods get the 'webmethod' attribute. When the webservice is compiled, the attributes are being read and the classes and methods with those attributes will be used to generate the service description and the code to access the webservice.
When you write your own controls, you can use attributes to show or hide them from the Visual Studio toolbox or determine which ones of the control's properties appear in the properties window.
When you intend a class for serialisation, you can use attributes to control which properties and public members are serialized and which are not.
Or, more general, attributes are simple data objects which give another class (which reads the attributes via reflection) information on how to use the class or its methods, properties or member variables. You can even write your own attributes for some purpose, but they will be of little use unless you write another class which looks for and uses those attributes when objects are passed as parameters.
And from the clouds a mighty voice spoke: "Smile and be happy, for it could come worse!"
And I smiled and was happy And it came worse.
|
|
|
|
|
great example of how they are already being used.
I guess what I was more curious about is an example of where I would want to develop my own attributes. I liked your example of it being used with reflection and to be honest I never gave this any thought with reflection.
would love to see an article talking about Attributes and using them in reflection. anyone care to shamelessly promote their article??
|
|
|
|
|
At work, we use a code generation utility and framework to automatically generate C# proxys for unmanaged C++ objects. We use attributes to detemine which properties/methods/classes from the C# interfaces bind to which underlying C++ methods/classes, etc. The generator produces the implementation automagically from the interface declarations.
I also used attributes for determining the design time behavior of custom controls in custom designers in WinForms.
|
|
|
|
|
Hi Dennis,
I've been using attributes extensively in nearly all my business objects for quite a while now and I find them to be most useful for general validation and integrity checks. I have a series of attributes that are purely business object related and these in turn help to reduce the repetitive nature of business objects and the general bloat of validation code.
Business object property validation[^]
An example of a custom attribute would be as follows:
Option Strict Off
Option Explicit On
Imports System.Reflection
Namespace Attributes
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property)> Public Class NotEmptyAttribute
Inherits attribBase
Implements ITest
Private mstrDependantPropName As String
Public Sub New(Optional ByVal strDependantPropName As String = "")
mstrDependantPropName = strDependantPropName
End Sub
Public Function GetRule() As String Implements ITest.GetRule
Return "The value cannot be a zero length (empty) string"
End Function
Public Function TestCondition(ByVal Value As Object, ByRef cls As Object) As Boolean Implements ITest.TestCondition
If Value Is Nothing Then Return True
Dim lStr As String = CType(Value, String)
If Not lStr.Equals(String.Empty) Then Return False
If mstrDependantPropName.Equals(String.Empty) Then
Return True
End If
Dim lStrPropType As String = String.Empty
Dim lObjVal As Object = Nothing
If SetLocalObjects(mstrDependantPropName, cls, lStrPropType, lObjVal) = False Then Return False
Return TestDependant(lStrPropType, lObjVal)
End Function
End Class
End Namespace
As you can this attribute inherits from a base class:
Option Strict Off
Option Explicit On
Imports System.Reflection
Namespace Attributes
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property)> Public Class attribBase
Inherits System.Attribute
Protected Overridable Function SetLocalObjects(ByVal propName As String, ByRef cls As Object, ByRef strPropType As String, ByRef objVal As Object) As Boolean
Dim t As Type = cls.GetType
Dim m As System.Reflection.MemberInfo() = t.GetMember(propName)
If m.Length < 1 Then Return False
Dim pi As PropertyInfo
Dim fi As FieldInfo
If TypeOf m(0) Is PropertyInfo Then
pi = t.GetProperty(propName)
strPropType = pi.PropertyType.ToString
objVal = pi.GetValue(cls, Nothing)
Else
fi = t.GetField(propName)
strPropType = fi.FieldType.ToString
objVal = fi.GetValue(cls)
End If
Return True
End Function
Protected Overridable Function TestDependant(ByVal strPropType As String, ByVal objVal As Object) As Boolean
Select Case strPropType
Case GetType(Boolean).ToString
If CType(objVal, Boolean).Equals(False) Then Return False
Case GetType(DateTime).ToString
If CType(objVal, DateTime).Equals(DateTime.MinValue) Then Return False
Case GetType(String).ToString
If CType(objVal, String).Equals(String.Empty) Then Return False
Case GetType(Guid).ToString
If CType(objVal, Guid).Equals(Guid.Empty) Then Return False
Case GetType(Int32).ToString
If CType(objVal, Int32).Equals(CType(0, Int32)) Then Return False
Case GetType(Int16).ToString
If CType(objVal, Int16).Equals(CType(0, Int16)) Then Return False
Case GetType(Byte).ToString
If CType(objVal, Byte).Equals(CType(0, Byte)) Then Return False
End Select
Return True
End Function
End Class
End Namespace
Hope this helps...
Edward Steward
edwardsteward@optusnet.com.au
|
|
|
|
|
Here's an example of the use of a custom Attribute: EnumTree[^]
|
|
|
|
|
Orm Mapping e.g.
[Table("MyFooTable")]
{
[Column("ColumnName", DbType.Varchar, 255)]
public string Bar {get; set}
[Column]
public string Baz {get; set}
}
The above psuedocode pretty much, but I have seen it done like this. There are similar things for serialization. You should try googling for ".net data annotations" - these are really useful for UI work.
|
|
|
|
|