C# Attributes: One Attribute to Rule Them All? - c#

Is it possible to assign an attribute on a property and use it in order to assign other attributes - doing so without using reflection?
The code:
public class CashierOut : BaseActivity
{
[Description("Flag indicates whether break to execution.")]
[DefaultValue(false)]
[MyCustomAttribute(ParameterGroups.Extended)]
public bool CancelExecution { get; set; }
[Description("Flag indicates whether allow exit before declation.")]
[DefaultValue(true)]
[MyCustomAttribute(ParameterGroups.Extended)]
[DisplayName("Exit before declaration?")]
public bool AllowExitBeforeDeclare { get; set; }
}
I would like to do something like this:
public class CashierOut : BaseActivity
{
[MyResourceCustom("CashierOut.CancelExecution")]
public bool CancelExecution { get; set; }
[MyResourceCustom("CashierOut.AllowExitBeforeDeclare")]
public bool AllowExitBeforeDeclare { get; set; }
}
public sealed class MyResourceCustom : Attribute
{
public string ResourcePath { get; private set; }
public ParameterGroupAttribute(string resourcePath)
{
ResourcePath = resourcePath;
// Get attributes attributes value from external resource using the path.
}
}

Attributes simply add meta data to the members they are defined on - by themselves they do nothing.
You will have to use reflection in order to produce some behaviour depending on the attribute values.
This is how all attributes work - some of the tooling is aware of some attributes (like the compiler and the ConditionalAttribute), but this is still done via reflection.

Look into Aspect Oriented Programming. You can use tools like postsharp to modify your code either at compile or runtime.

You could add a member to MyResourceCustom that wraps Description, DefaultValue, and MyCustomAttribute in an immutable instance (maybe even a static global, if it can be the same for everyone).
public class MyResourceCustom : Attribute {
public MyResourceCustomDescriptor Descriptor { get; private set; }
public MyResourceCustom(MyResourceCustomDescriptor descriptor)
: base() {
Descriptor = descriptor;
}
public class MyResourceCustomDescriptor {
public string Description { get; private set; }
public bool DefaultValue { get; private set; }
public ParameterGroups ParameterGroup { get; private set; }
public MyResourceCustomDescriptor(string path) {
// read from path
}
}
public class MyAdvancedResouceCustomDescriptor : MyResourceCustomDescriptor {
public string DisplayName { get; private set; }
// etc...
}
When you fetch the attribute you can get its Descriptor property and read the values.
As a sidenote, you should name it IsDefaultValue.

Related

Using empty base class just for polymorphism purpose?

I have my ResponseDto which includes a simple string property named Answer.
public string Answer { get; set; }
Now, the requirement came such that I could either be getting an answer as a string, or as an array of int.
I decided to create two classes for this:
public class AnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType {
public int[] AnswerOptionIds { get; set; }
}
I could serialize / deserialize it accordingly.
But to still keep a single response property type, I thought about creating an empty base class:
public class BaseAnswerType { }
public class AnswerType : BaseAnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType : BaseAnswerType {
public Guid[] AnswerOptionIds { get; set; }
}
and change my property in ResponseDto to:
public BaseAnswerType Answer { get; set }
through which via run time, I would be returning either of the two classes.
Is this a bad approach? Any alternate would be greatly appreciated.

Override the [Required] properties attributes in a wrapper/composition class

There is the following class:
public class A
{
[Required]
public string property { get; set; }
}
and it's used by another class like:
public class B
{
public A prop { get; set; }
public A prop2 { get; set; }
}
in my scenario, B.prop.property should be required while B.prop2.property should not be [Required].
Is there a way to override prop2.property attribute to be not required? and it also should affect the record recorded in the Database?
if not what is the most recommended practice to deal with such issue?
No. There is no way to achieve what you're talking about. You can do so via inheritance. For example:
public class C : A
{
public new string property { get; set; }
}
Then:
public class B
{
public A prop { get; set; }
public C prop2 { get; set; }
}
In other words, the property must literally be a type where that property is not required. You can't just disable an attribute on a class instance at a whim.

Implement property with custom attribute

Maybe I'm getting the Concept of Custom Attributes wrong, but I thought this should be something that would be possible:
I'm having a class with a string property. I have multiple derived classes with properties that basically get a sub-string of the baseClass's property
class BaseClass {
public string MyString { get; set;}
}
class FooClass : BaseClass {
public string Part1 { get { return MyString.SubString(0,3); }}
public string Part2 { get { return MyString.SubString(3,5); }}
}
class BarClass : BaseClass {
public string PartA { get { return MyString.SubString(0,4); }}
public string PartB { get { return MyString.SubString(4,1); }}
}
They also have a setters, and the real code is a little bit more complex... But you got the picture.
I would like to not have to implement this a thousand times, so I was thinking of using a custom attribute. So I could do:
class FooClass : BaseClass {
[DataPart(0, Length = 3)]
public string Part1 { get; set; }
[DataPart(3, Length = 5)]
public string Part2 { get; set; }
}
class BarClass : BaseClass {
[DataPart(4, Length = 4)]
public string PartA { get; set; }
[DataPart(4)]
public string PartB { get; set; }
}
I already have Custom Attribute for it:
[global::System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
sealed class DataPartAttribute : Attribute
{
public ushort Position { get; private set; }
public ushort Length { get; set; }
public DataByteAttribute(ushort position)
{
Position = position;
}
}
What now?
You need to write code to process your custom DataPartAttribute by using reflection.
perhaps, you should think about using System.ComponentModel.DataAnnotations.
For ex)
[StringLength(100, MinimumLength =30)]
public string Description { get; set; }
you can then use an instance of ObjectValidator to validate your object.
You would need to have the attribute hijack the getter to return something custom, which is not possible to do with .NET alone.
Apparently you can do it with a product called PostSharp (see the answer to this question).
You could put code in the getter to look at the attribute and build the string accordingly, so that every property would have the exact same code. I suspect this is all that PostSharp would do. But it would perform worse than using .Substring in every getter.

Can I retrieve a property value from a parent model, without a DB relationship between child and parent?

I have an application serving multiple websites and would like to setup colour scheming like this:
Each element (link, text, heading, etc) has a default for the application
Each element can be overridden for individual websites
If an element is set to application default, the custom colour should be remembered for future reference
Website Configuration.cs
public class WebsiteConfiguration
{
public ApplicationConfiguration ApplicationConfiguration { get; set; }
public string CustomLinkColour { get; set; }
public bool IsCustomLinkColourActive { get; set; }
public string LinkColour
{
get
{
return (IsCustomLinkColourActive ? CustomLinkColour : ApplicationConfiguration.DefaultLinkColour);
}
}
public string CustomTextColour { get; set; }
public bool IsCustomTextColourActive { get; set; }
public string TextColour
{
get
{
return (IsCustomTextColourActive ? CustomTextColour : ApplicationConfiguration.DefaultTextColour);
}
}
// ...and so on for each colour scheme element...
}
ApplicationConfiguration.cs
public class ApplicationConfiguration
{
public List<WebsiteConfiguration> WebsiteConfigurations { get; set; }
public string DefaultLinkColour { get; set; }
public string DefaultTextColour { get; set; }
//... and so on for each colour scheme element...
}
Problems
It's a lot of work!
There are just 2 colour scheme elements in the examples above, but there may be 50+ of them.
Also, it is creating a lot of work in the view files, with if else blocks etc.
Attempted Solution
A ColourSchemeItem class manages the logic.
public class ColourSchemeItem
{
public string DefaultColour { get; set; }
public string CustomColour { get; set; }
public bool IsCustomColourActive { get; set; }
public string ActiveColour
{
get
{
return (IsCustomColourActive ? CustomColour : DefaultColour);
}
}
}
And then WebsiteConfiguration becomes much simpler...
public class WebsiteConfiguration
{
public ApplicationConfiguration ApplicationConfiguration { get; set; }
public ColourSchemeItem Link { get; set; }
public ColourSchemeItem Text { get; set; }
// ...and so on for each colour scheme element...
}
However...
But somehow I need to get the default colour from the ApplicationConfiguration into the ColourSchemeItem. And I can't figure out how.
If the ColourSchemeItem contains a reference to it's parent - WebsiteConfiguration - I get a No Key Defined for Entity error.
If ColourSchemeItem does NOT contain a reference to it's parent, I can't access the default colour from WebsiteConfiguration.ApplicationConfiguration.
The only other option I can think of it to access the DB directly from within the ColourSchemeItemclass. If there are going to be 50+ of these, I don't want to do that.
Create a custom constructor, and set the default to AplicationConfiguration

How do I get Aspects applied to certain element (e.g. for LocationInfo type parameter, or PropertyInfo)?

TL/DR:
How do I get Aspects applied to certain element, e.g. for LocationInfo type parameter, or PropertyInfo ?
Elaboration:
I want to find all dependencies of properties/fields during RuntimeInitialize
[Serializable]
public class NotifyPropertyChangedAspect : LocationInterceptionAspect {
/* ... stuff ... */
public override void RuntimeInitialize(LocationInfo locationInfo)
{
foreach(var attr in locationInfo.PropertyInfo.GetCustomAttributes(false)
.OfType<DependsOnAttribute>();){
this.SaveDependencies(attr.Dependencies);
}
base.RuntimeInitialize(locationInfo);
}
/* ... things ... */
}
The code above works for attributes:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DependsOnAttribute : Attribute {
public string[] Dependencies { get; set; }
public DependsOnAttribute(params string[] dependencies) { Dependencies = dependencies; }
}
[NotifyPropertyChangedAspect]
class MyClass {
public string Name { get; set; }
[DependsOnAttribute("Name")]
public bool IsChanged { get; set; }
}
But not for aspects:
[Serializable]
public class DependsOnAspect : LocationLevelAspect {
public string[] Dependencies { get; set; }
public DependsOnAspect(params string[] dependencies) { Dependencies = dependencies; }
}
[NotifyPropertyChangedAspect]
class MyClass {
public string Name { get; set; }
[DependsOnAspect("Name")]
public bool IsChanged { get; set; }
}
Aspects are special case of Multicast attributes. In brevity, Multicast attributes are removed from the assembly after it is processed by PostSharp. To override this behavior, you need to specify the following:
[MulticastAttributeUsage(PersistMetaData = true)]
public class DependsOnAspect : LocationLevelAspect { ... }
This will tell PostSharp that you need the aspect attribute to be present at runtime.

Categories