Jak utworzyć atrybut niestandardowy w C #

119

Próbowałem wiele razy, ale nadal nie jestem w stanie zrozumieć użycia atrybutów niestandardowych (przeszedłem już przez wiele linków).

Czy ktoś może mi wyjaśnić bardzo podstawowy przykład atrybutu niestandardowego z kodem?

slash shogdhe
źródło

Odpowiedzi:

96

Chociaż kod służący do tworzenia niestandardowego atrybutu jest dość prosty, bardzo ważne jest, aby zrozumieć, czym są atrybuty:

Atrybuty to metadane wkompilowane w Twój program. Same atrybuty nie dodają żadnej funkcjonalności do klasy, właściwości czy modułu - tylko dane. Jednak korzystając z refleksji, można wykorzystać te atrybuty w celu stworzenia funkcjonalności.

Spójrzmy na przykład na Validation Application Block z biblioteki Enterprise Library firmy Microsoft . Jeśli spojrzysz na przykładowy kod, zobaczysz:

    /// <summary>
    /// blah blah code.
    /// </summary>
    [DataMember]
    [StringLengthValidator(8, RangeBoundaryType.Inclusive, 8, RangeBoundaryType.Inclusive, MessageTemplate = "\"{1}\" must always have \"{4}\" characters.")]
    public string Code { get; set; }

Z powyższego fragmentu można się domyślić, że kod będzie zawsze walidowany, po każdej zmianie, zgodnie z regułami Walidatora (w przykładzie ma co najmniej 8 znaków i maksymalnie 8 znaków). Ale prawda jest taka, że ​​Atrybut nic nie robi; jak wspomniano wcześniej, dodaje tylko metadane do właściwości.

Jednak biblioteka korporacyjna ma Validation.Validatemetodę, która zajrzy do twojego obiektu i dla każdej właściwości sprawdzi, czy zawartość narusza regułę informowaną przez atrybut.

Tak więc powinieneś myśleć o atrybutach - sposobie dodawania danych do kodu, które mogą być później wykorzystane przez inne metody / klasy / itp.

Bruno Brant
źródło
czy naprawdę polubię odpowiedź, a szczególnie ", jeszcze jedno pytanie mogę umieścić ten sam warunek w zestawieniu powyższego kodu, więc jak różni się on od atrybutów,
slash shogdhe
1
@slash: Czy możesz to przeformułować? Nie całkiem zrozumiałem pytanie.
Bruno Brant
1
Myślę, że slash miał na celu zapytać o różnicę między używaniem atrybutów a umieszczeniem rzeczywistego kodu walidacyjnego wewnątrz ustawiacza właściwości. Odpowiedź: Podczas pisania kodu wewnątrz ustawiającego można wykonać walidację wartości, użycie samych atrybutów nie spowoduje wykonania walidacji jako takiej. Atrybuty to po prostu „metadane”. Inny kod w innym miejscu powinien być zainteresowany używanymi atrybutami, czytać je i wykonywać na ich podstawie akcje. Typowym przykładem jest biblioteka walidacyjna, o czym wspomniał @BrunoBrant.
romar
10
Nie wiem, dlaczego jest to akceptowana odpowiedź. Rzeczywiste pytanie (które jest również indeksowane w Google) brzmi: „Jak utworzyć atrybut niestandardowy w C #”. Odpowiedzi wcale nie zagłębiają się w ten temat. Z drugiej strony tak jest.
Drakestar
Myślę, że druga odpowiedź jest bardziej związana z pytaniem.
Mohammad Taherian
267

Zaczynasz od napisania klasy, która pochodzi od Attribute :

public class MyCustomAttribute: Attribute
{
    public string SomeProperty { get; set; }
}

Następnie możesz udekorować wszystko (klasę, metodę, właściwość, ...) tym atrybutem:

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{

}

i na koniec użyłbyś refleksji, aby go pobrać:

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // TODO: Do something with the value
}

Możesz ograniczyć typy docelowe, do których można zastosować ten atrybut niestandardowy, używając atrybutu AttributeUsage :

/// <summary>
/// This attribute can only be applied to classes
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute

Ważne informacje o atrybutach:

  • Atrybuty to metadane.
  • Są one umieszczane w zestawie w czasie kompilacji, co ma bardzo poważne konsekwencje dotyczące tego, jak można ustawić ich właściwości. Akceptowane są tylko wartości stałe (znane w czasie kompilacji)
  • Jedynym sposobem nadania sensu i wykorzystania atrybutów niestandardowych jest użycie odbicia . Więc jeśli nie używasz odbicia w czasie wykonywania, aby je pobrać i ozdobić coś niestandardowym atrybutem, nie spodziewaj się zbyt wiele.
  • Czas powstania atrybutów jest niedeterministyczny. Są tworzone przez CLR i nie masz nad nimi żadnej kontroli.
Darin Dimitrov
źródło
3
Gdzie, w której funkcji / klasie mam „użyć odbicia, aby ją pobrać”
Hasan A Yousef
@Hasan A Yousef, na przykład w Entity Framework istnieje atrybut „Key”, który mówi do frameworka: Ta właściwość powinna być uznawana za klucz podstawowy. W tworzeniu ORM atrybuty są bardzo pomocne
Parsa
Jak uzyskać dostęp do atrybutu niestandardowego właściwości, a nie klasy?
Canvas
docs.microsoft.com/en-us/dotnet/standard/attributes/ ... tylko dla kompletności, ta strona msdn podsumowuje to bardzo dobrze
Barış Akkurt
W przypadku leków generycznych uzyskanie typów jest dużo, dużo łatwiejsze:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh
27

Wykorzystując / kopiując świetną odpowiedź Darina Dimitrova , oto jak uzyskać dostęp do niestandardowego atrybutu na właściwości, a nie w klasie:

Urządzony majątek [klasy Foo]:

[MyCustomAttribute(SomeProperty = "This is a custom property")]
public string MyProperty { get; set; }

Pobieram to:

PropertyInfo propertyInfo = typeof(Foo).GetProperty(propertyToCheck);
object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
if (attribute.Length > 0)
{
    MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
    string propertyValue = myAttribute.SomeProperty;
}

Możesz wrzucić to do pętli i użyć odbicia, aby uzyskać dostęp do tego niestandardowego atrybutu dla każdej właściwości klasy Foo:

foreach (PropertyInfo propertyInfo in Foo.GetType().GetProperties())
{
    string propertyName = propertyInfo.Name;

    object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
    // Just in case you have a property without this annotation
    if (attribute.Length > 0)
    {
        MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
        string propertyValue = myAttribute.SomeProperty;
        // TODO: whatever you need with this propertyValue
    }
}

Wielkie dzięki, Darin !!

Zbiornik
źródło
w jaki sposób moglibyśmy to rozszerzyć, jeśli nie wiemy, jakie typy atrybutów istnieją we właściwości? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Chcę tylko m1()
powtórzyć
0

Krótka odpowiedź jest taka, aby utworzyć atrybut w c # wystarczy odziedziczyć go z klasy Attribute, tylko to :)

Ale tutaj opiszę szczegółowo atrybuty:

w zasadzie atrybuty to klasy, których możemy użyć do zastosowania naszej logiki do zestawów, klas, metod, właściwości, pól, ...

W .Net firma Microsoft udostępniła pewne predefiniowane atrybuty, takie jak atrybuty przestarzałe lub walidacyjne, takie jak ([Wymagane], [StringLength (100)], [Zakres (0, 999,99)]). Mamy również atrybuty, takie jak ActionFilters w asp.net które mogą być bardzo przydatne do zastosowania naszej pożądanej logiki do naszych kodów (przeczytaj ten artykuł o filtrach akcji, jeśli jesteś pasjonatem, aby się go uczyć)

Po drugie, możesz zastosować rodzaj konfiguracji do swojego atrybutu za pośrednictwem AttibuteUsage.

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]

Kiedy dekorujesz klasę atrybutu za pomocą AttributeUsage, możesz powiedzieć kompilatorowi C #, gdzie mam zamiar użyć tego atrybutu: zamierzam używać tego na klasach, w zestawach we właściwościach lub na ... i mój atrybut może być używany kilka razy na zdefiniowanych celach (klasy, zespoły, właściwości, ...) czy nie ?!

Po tej definicji atrybutów pokażę wam przykład: Wyobraź sobie, że chcemy zdefiniować nową lekcję na uniwersytecie i chcemy, aby tylko administratorzy i mistrzowie na naszej uczelni mogli zdefiniować nową lekcję, OK?

namespace ConsoleApp1
{
    /// <summary>
    /// All Roles in our scenario
    /// </summary>
    public enum UniversityRoles
    {
        Admin,
        Master,
        Employee,
        Student
    }

    /// <summary>
    /// This attribute will check the Max Length of Properties/fields
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
    public class ValidRoleForAccess : Attribute
    {
        public ValidRoleForAccess(UniversityRoles role)
        {
            Role = role;
        }
        public UniversityRoles Role { get; private set; }

    }


    /// <summary>
    /// we suppose that just admins and masters can define new Lesson
    /// </summary>
    [ValidRoleForAccess(UniversityRoles.Admin)]
    [ValidRoleForAccess(UniversityRoles.Master)]
    public class Lesson
    {
        public Lesson(int id, string name, DateTime startTime, User owner)
        {
            var lessType = typeof(Lesson);
            var validRolesForAccesses = lessType.GetCustomAttributes<ValidRoleForAccess>();

            if (validRolesForAccesses.All(x => x.Role.ToString() != owner.GetType().Name))
            {
                throw new Exception("You are not Allowed to define a new lesson");
            }
            
            Id = id;
            Name = name;
            StartTime = startTime;
            Owner = owner;
        }
        public int Id { get; private set; }
        public string Name { get; private set; }
        public DateTime StartTime { get; private set; }

        /// <summary>
        /// Owner is some one who define the lesson in university website
        /// </summary>
        public User Owner { get; private set; }

    }

    public abstract class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }


    public class Master : User
    {
        public DateTime HireDate { get; set; }
        public Decimal Salary { get; set; }
        public string Department { get; set; }
    }

    public class Student : User
    {
        public float GPA { get; set; }
    }



    class Program
    {
        static void Main(string[] args)
        {

            #region  exampl1

            var master = new Master()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                Department = "Computer Engineering",
                HireDate = new DateTime(2018, 1, 1),
                Salary = 10000
            };
            var math = new Lesson(1, "Math", DateTime.Today, master);

            #endregion

            #region exampl2
            var student = new Student()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                GPA = 16
            };
            var literature = new Lesson(2, "literature", DateTime.Now.AddDays(7), student);
            #endregion

            ReadLine();
        }
    }


}

W prawdziwym świecie programowania być może nie używamy tego podejścia do używania atrybutów i powiedziałem to z powodu jego edukacyjnego punktu w używaniu atrybutów

Hamid
źródło