简体   繁体   中英

Understanding Attributes in c#

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?

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);

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.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM