Reputation: 109
I came across this piece of code while studying attributes
using System;
public class HelpAttribute : Attribute
{
public HelpAttribute(String Descrition_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
[Help("this is a do-nothing class")]
public class AnyClass
{
}
The doubt I am having is that, when this
keyword is being used.
As we know, this
refers to the current object.
I don't see any object being created here?
How will the constructor
really work here?
Upvotes: 1
Views: 138
Reputation: 6749
I like the already given answer and just want to throw a little extra in there; don't mark this as the answer just use it as a helper if needed.
The attribute doesn't become part of the class, it just get's tied to the class. In other words, anyone using AnyClass
will not care or know about the HelpAttribute
unless they specifically want / need to know; and when they do they use code like what's listed in Patrick's answer to get that information.
A common question is why use them and how to properly use them. Just remember attributes don't really modify any existing objects that the attribute but rather it's for compiling and other objects that care about using the attribute. I'm going to post an example.
NOTE: This example is just for educational purposes.
In this example notice there are two methods that call on the workers. The first runs the workers and cares less about attributes, the second orders the workers by the custom PriorityAttribute
. Also, I apologize this is all in one file, which isn't ideal. I just did it that way to make it easier to post here. However you can copy and paste this into a console app and it should run just fine. I am using VS2017, .NET 4.7.1, and C# 7.2 if it matters any.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static System.Console;
namespace ConsoleApp1
{
class Program
{
public const int MaxNumberOfTasks = 10;
private static readonly Random Random = new Random();
static void Main(string[] args)
{
var workers = new List<IWorker>(MaxNumberOfTasks);
for (var i = 0; i < MaxNumberOfTasks; i++)
workers.Add(GetRandomWorker());
WriteLine("Random Priority Workers\n");
RunWorkersAsync(workers).Wait();
WriteLine("\nSet Priority Workers\n");
RunWorkersByPriorityAsync(workers).Wait();
WriteLine("\nWork Complete\n");
Read();
}
private static async Task RunWorkersAsync(List<IWorker> workers)
{
foreach (var worker in workers)
await worker.DoWork();
}
private static async Task RunWorkersByPriorityAsync(List<IWorker> workers)
{
var highWorkers = new List<IWorker>();
var mediumWorkers = new List<IWorker>();
var lowWorkers = new List<IWorker>();
foreach (var worker in workers)
{
var priorityAttribute = (PriorityAttribute)worker.GetType().GetCustomAttributes(typeof(PriorityAttribute), false).FirstOrDefault();
if (priorityAttribute != null)
{
switch (priorityAttribute.Priority)
{
case Priority.High:
highWorkers.Add(worker);
break;
case Priority.Medium:
mediumWorkers.Add(worker);
break;
case Priority.Low:
default:
lowWorkers.Add(worker);
break;
}
}
else
{
lowWorkers.Add(worker);
}
}
await RunWorkersAsync(highWorkers);
await RunWorkersAsync(mediumWorkers);
await RunWorkersAsync(lowWorkers);
}
private static IWorker GetRandomWorker()
{
var randomNumber = Random.Next(0, 3);
switch (randomNumber)
{
case 0:
return new HighLevelWorker();
case 1:
return new MediumLevelWorker();
case 2:
default:
return new LowLevelWorker();
}
}
}
public interface IWorker
{
Task DoWork();
}
[AttributeUsage(AttributeTargets.Class)]
public class PriorityAttribute : Attribute
{
public PriorityAttribute(Priority priority) => Priority = priority;
public Priority Priority { get; }
}
public enum Priority
{
Low,
Medium,
High
}
[Priority(Priority.High)]
public class HighLevelWorker : IWorker
{
public async Task DoWork()
{
await Task.Delay(200);
WriteLine($"{nameof(HighLevelWorker)} complete.");
}
}
[Priority(Priority.Medium)]
public class MediumLevelWorker : IWorker
{
public async Task DoWork()
{
await Task.Delay(200);
WriteLine($"{nameof(MediumLevelWorker)} complete.");
}
}
[Priority(Priority.Low)]
public class LowLevelWorker : IWorker
{
public async Task DoWork()
{
await Task.Delay(200);
WriteLine($"{nameof(LowLevelWorker)} complete.");
}
}
}
So there is the console app and here is the output:
Random Priority Workers
MediumLevelWorker complete.
MediumLevelWorker complete.
HighLevelWorker complete.
LowLevelWorker complete.
HighLevelWorker complete.
LowLevelWorker complete.
MediumLevelWorker complete.
MediumLevelWorker complete.
HighLevelWorker complete.
MediumLevelWorker complete.
Set Priority Workers
HighLevelWorker complete.
HighLevelWorker complete.
HighLevelWorker complete.
MediumLevelWorker complete.
MediumLevelWorker complete.
MediumLevelWorker complete.
MediumLevelWorker complete.
MediumLevelWorker complete.
LowLevelWorker complete.
LowLevelWorker complete.
Work Complete
Things to take away from this. All of the worker objects do exactly the same thing; they are named differently but they are identical in code and implement the same interface; the only difference is the PriorityAttribute
applied to each. You can remove the attribute and both RunWorkersAsync
and RunWorkerByPriorityAsync
tasks will do the same thing. Also, if you change the priority on any of the attributes you'll notice it follows regardless of the name of the class. In other words, we can operate with or without the priority as well as modifying the priority when needed (although in this example modifying will not make sense via the naming; but again this was just for educational purposes.)
Upvotes: 2
Reputation: 156938
I don't see any object being created here?
Well, there is. When you request the attributes from AnyClass
using reflection, it creates a new instance of the attribute with the specified parameters.
Actually the attribute will be created just like any other class, just the syntax is different to specify an attribute.
Note that the attribute will not be called automatically on the constructor of AnyClass
!
Type t = typeof(AnyClass);
HelpAttribute[] attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
.Cast<HelpAttribute>()
.ToArray(); // on this line the attribute is instantiated
Console.WriteLine(attributes.FirstOrDefault()?.Description);
Upvotes: 4