Say I have a class with any number of properties of any type
public class Test
{
public string A {get;set;}
public object B {get;set;}
public int C {get;set;}
public CustomClass D {get;set;}
}
I want all of the objects inside this class to have a notion of 'errors' and 'warnings'. For example, based on some condition in another class, I might want to set a warning on property A, and then display that information in the GUI. The GUI is not my concern; rather how should I set this warning? I would love to be able to do something like:
For every property in a class, add a 'Warning' and 'Error' property such that I can do..
Test t = new Test();
t.A.Warning = "This is a warning that the GUI will know to display in yellow".
t.B.Error = null;
What is the best way to go about doing this? It would be nice if I could add a custom attribute to each property of the class that would add these additional properties and allow me to access them in a clear way.
I have seen solutions that add a Dictionary to the parent class (Test), and then pass in strings that match the property names, or use reflection to get the property name and pass that in, but I would prefer something cleaner.
You could add a custom attribute that you want to the property and then use an extension method on object to access those attributes.
Something like this should work
First you will need to create your attribute class
[AttributeUsage(AttributeTargets.All/*, AllowMultiple = true*/)]
public class WarningAttribute : System.attribute
{
public readonly string Warning;
public WarningAttribute(string warning)
{
this.Warning = warning;
}
}
More Reading Here
Use it as such
[WarningAttribute("Warning String")]
public string A {get;set;}
Then access it as so MSDN Article
public static string Warning(this Object object)
{
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(object);
foreach (System.Attribute attr in attrs)
{
if (attr is WarningAttrbiute)
{
return (WarningAttribute)attr.Warning;
}
}
}
Then if you have an item that you want to access the warning on you can simply call
test.A.Warning;
If you're wanting to set the warning string you could implement some kind of setter for that a little more cleanly. Potentially through setting a secondary object or property type.
An alternative method to do this would be instead of just using string
and object
you could create a custom generic type to handle that property setting.
Something like
public class ValidationType<T>
{
public T Value {get; set;}
public string Warning {get; set;}
public string Error {get; set;}
public ValidationType(T value)
{
Value = value;
}
}
Use like
var newWarning = new ValidationType<string>("Test Type");
newWarning.Warning = "Test STring";
Console.WriteLine("newWarning.Value");
If you could get away with a method instead of property, than you could create an extension for object - but then it would be hard to narrow it's use just for some classes.
Something like:
public static void Warning(this object target, string message)
{
...
}
public static String GetWarning(this object target)
{
}
Of course it would be hard to maintain such a warning in the object's state, but then you could use some dictionaries, etc.
Rather than trying to attribute the properties, I would suggest adding a collection of error/warning properties to a base class that your business objects inherit from.
This way, you can provide more detailed information, you can remove them when they have been displayed to the user, and, if you are sending your data out over "the wire" (web services, wcf services), the errors can travel with your objects instead of requiring special processing to send the attributes.
Firstly, attributes can't modify the object their placed on. Unless you combine them with AOP. I would suggest using a wrapper class around the class by inheriting it, provided the classes aren't sealed. If they are sealed, then you'll have to write an actual wrapper class, passing off all operations, except the ones your adding, to a private field that is an instance of the class your wrapping.
Edit:
For ease of use, the extension method aproach that was suggested is probably the best solution.