วิธีสร้างแอตทริบิวต์ที่กำหนดเองใน C #


119

ฉันได้ลองหลายครั้งแล้ว แต่ก็ยังไม่เข้าใจการใช้คุณลักษณะที่กำหนดเอง (ฉันได้ดูลิงก์มากมายแล้ว)

ใครช่วยอธิบายตัวอย่างพื้นฐานของแอตทริบิวต์ที่กำหนดเองพร้อมรหัสให้ฉันได้ไหม

คำตอบ:


96

แม้ว่าโค้ดในการสร้างแอตทริบิวต์ที่กำหนดเองจะค่อนข้างเรียบง่าย แต่สิ่งสำคัญมากที่คุณต้องเข้าใจว่าแอตทริบิวต์คืออะไร

แอตทริบิวต์เป็นข้อมูลเมตาที่รวบรวมไว้ในโปรแกรมของคุณ แอตทริบิวต์เองไม่ได้เพิ่มฟังก์ชันการทำงานใด ๆ ให้กับคลาสคุณสมบัติหรือโมดูล - เพียงแค่ข้อมูล อย่างไรก็ตามการใช้การสะท้อนกลับสามารถใช้ประโยชน์จากคุณสมบัติเหล่านั้นเพื่อสร้างฟังก์ชันการทำงานได้

ดังนั้นสำหรับตัวอย่างให้ดูที่การตรวจสอบบล็อกโปรแกรมประยุกต์จากไมโครซอฟท์โครงการห้องสมุด หากคุณดูตัวอย่างโค้ดคุณจะเห็น:

    /// <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; }

จากตัวอย่างด้านบนอาจมีคนเดาได้ว่าโค้ดจะได้รับการตรวจสอบความถูกต้องเสมอทุกครั้งที่มีการเปลี่ยนแปลงตามกฎของ Validator (ในตัวอย่างมีอักขระอย่างน้อย 8 ตัวและไม่เกิน 8 อักขระ) แต่ความจริงก็คือ Attribute ไม่ได้ทำอะไรเลย ดังที่ได้กล่าวไว้ก่อนหน้านี้จะเพิ่มเฉพาะข้อมูลเมตาให้กับคุณสมบัติเท่านั้น

อย่างไรก็ตามไลบรารีขององค์กรมีValidation.Validateวิธีการที่จะตรวจสอบวัตถุของคุณและสำหรับแต่ละคุณสมบัติจะตรวจสอบว่าเนื้อหาละเมิดกฎที่แอตทริบิวต์แจ้งหรือไม่

นั่นคือวิธีที่คุณควรคิดเกี่ยวกับแอตทริบิวต์ - วิธีเพิ่มข้อมูลลงในโค้ดของคุณซึ่งอาจใช้ในภายหลังโดยวิธีการ / คลาส / ฯลฯ อื่น ๆ


ฉันจะชอบคำตอบหรือไม่และเป็นพิเศษ "อีกหนึ่งคำถามที่ฉันสามารถใส่เงื่อนไขเดียวกันในชุดคำสั่งของรหัสด้านบนเพื่อให้มันแตกต่างจากแอตทริบิวต์อย่างไร
slash shogdhe

1
@slash: คุณสามารถเรียบเรียงใหม่ได้หรือไม่? ฉันไม่ค่อยเข้าใจคำถาม
Bruno Brant

1
ฉันคิดว่าเครื่องหมายทับหมายถึงการถามเกี่ยวกับความแตกต่างระหว่างการใช้แอตทริบิวต์และการใส่รหัสตรวจสอบความถูกต้องที่แท้จริงภายในตัวตั้งค่าคุณสมบัติ คำตอบ: ในขณะที่เขียนโค้ดภายใน setter สามารถทำได้เพื่อตรวจสอบความถูกต้องของค่า แต่การใช้แอตทริบิวต์เพียงอย่างเดียวจะไม่ทำการตรวจสอบความถูกต้องเช่นนี้ แอตทริบิวต์เป็นเพียง "ข้อมูลเมตา" โค้ดอื่นที่อื่นควรสนใจในแอตทริบิวต์ที่คุณใช้อ่านและดำเนินการตามพวกเขา ตัวอย่างทั่วไปคือไลบรารีการตรวจสอบความถูกต้องตามที่ @BrunoBrant กล่าวถึง
romar

10
ไม่แน่ใจว่าเหตุใดจึงเป็นคำตอบที่ยอมรับ คำถามจริง (ซึ่งจัดทำดัชนีใน Google เช่นกัน) คือ "วิธีสร้างแอตทริบิวต์ที่กำหนดเองใน C #" คำตอบไม่ได้เจาะลึกไปที่หัวข้อนั้นเลย ในทางกลับกันคำตอบที่ 2
Drakestar

ฉันคิดว่าคำตอบที่สองเกี่ยวข้องกับคำถามมากกว่า
Mohammad Taherian

267

คุณเริ่มต้นด้วยการเขียนคลาสที่มาจากแอตทริบิวต์ :

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

จากนั้นคุณสามารถตกแต่งอะไรก็ได้ (คลาสวิธีการคุณสมบัติ ... ) ด้วยแอตทริบิวต์นี้:

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

}

และสุดท้ายคุณจะใช้การสะท้อนกลับเพื่อดึงมันมา:

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
}

คุณสามารถ จำกัด ประเภทเป้าหมายที่จะใช้แอตทริบิวต์ที่กำหนดเองนี้ได้โดยใช้AttributeUsage :

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

สิ่งสำคัญที่ควรทราบเกี่ยวกับคุณลักษณะ:

  • แอตทริบิวต์เป็นข้อมูลเมตา
  • พวกเขาถูกอบเข้าในแอสเซมบลีในเวลาคอมไพล์ซึ่งมีผลกระทบอย่างมากว่าคุณจะตั้งค่าคุณสมบัติของมันได้อย่างไร ยอมรับเฉพาะค่าคงที่ (ทราบในเวลาคอมไพล์)
  • วิธีเดียวที่จะทำให้ความรู้สึกและการใช้แอตทริบิวต์ที่กำหนดเองใด ๆ คือการใช้การสะท้อน ดังนั้นหากคุณไม่ใช้การสะท้อนที่รันไทม์เพื่อดึงข้อมูลและตกแต่งบางอย่างด้วยแอตทริบิวต์ที่กำหนดเองอย่าคาดหวังว่าจะเกิดขึ้นมากนัก
  • เวลาของการสร้างแอตทริบิวต์ไม่ได้ถูกกำหนด พวกเขาสร้างอินสแตนซ์โดย CLR และคุณไม่สามารถควบคุมได้อย่างแน่นอน

3
โดยที่ฟังก์ชัน / คลาสใดที่ฉันจะ `ใช้การสะท้อนเพื่อดึงมันมา`
Hasan A Yousef

@Hasan A Yousef ตัวอย่างเช่นใน Entity Framework จะมีแอตทริบิวต์ "Key" ที่บอกกับ framework: คุณสมบัตินี้ควรถือว่าเป็นคีย์หลัก ในการสร้าง ORM แอตทริบิวต์มีประโยชน์มาก
Parsa

คุณเข้าถึงแอตทริบิวต์ที่กำหนดเองในคุณสมบัติไม่ใช่คลาสได้อย่างไร
Canvas

docs.microsoft.com/en-us/dotnet/standard/attributes/…เพื่อความสมบูรณ์หน้าmsdnนี้สรุปได้เป็นอย่างดี
Barış Akkurt

ด้วยยาชื่อสามัญมันง่ายกว่ามากที่จะได้รับประเภท:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh

27

การใช้ / คัดลอกคำตอบที่ยอดเยี่ยมของ Darin Dimitrovนี่คือวิธีเข้าถึงแอตทริบิวต์ที่กำหนดเองในคุณสมบัติไม่ใช่คลาส:

ทรัพย์สินที่ได้รับการตกแต่ง [ของชั้นเรียนFoo]:

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

ดึงมัน:

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

คุณสามารถโยนสิ่งนี้ในการวนซ้ำและใช้การสะท้อนเพื่อเข้าถึงแอตทริบิวต์ที่กำหนดเองนี้ในแต่ละคุณสมบัติของคลาส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
    }
}

ขอบคุณมากครับคุณดาริน !!


เราจะขยายสิ่งนี้ได้อย่างไรหากเราไม่รู้ว่าคุณสมบัติประเภทใดที่มีอยู่ในคุณสมบัติ object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);ฉันแค่อยากจะวนซ้ำพวกมันทั้งหมดและเรียกเมธอดm1()ของแต่ละแอตทริบิวต์ที่ไม่รู้จัก
heyNow

0

คำตอบสั้น ๆ คือการสร้างแอตทริบิวต์ใน c # คุณจะต้องสืบทอดจากคลาส Attribute เพียงเท่านี้ :)

แต่ที่นี่ฉันจะอธิบายรายละเอียดคุณลักษณะ:

โดยทั่วไปแอตทริบิวต์คือคลาสที่เราสามารถใช้เพื่อใช้ตรรกะของเรากับแอสเซมบลีคลาสเมธอดคุณสมบัติฟิลด์ ...

ใน. Net Microsoft ได้จัดเตรียมแอตทริบิวต์ที่กำหนดไว้ล่วงหน้าเช่นแอตทริบิวต์ที่ล้าสมัยหรือการตรวจสอบความถูกต้องเช่น ([จำเป็น], [StringLength (100)], [Range (0, 999.99)]) นอกจากนี้เรายังมีแอตทริบิวต์ประเภทต่างๆเช่น ActionFilters ใน asp.net ที่สามารถเป็นประโยชน์มากสำหรับการใช้ตรรกะที่ต้องการของเราที่จะรหัสของเรา (อ่านนี้บทความเกี่ยวกับตัวกรองการกระทำถ้าคุณมีความกระตือรือร้นที่จะเรียนรู้มัน)

อีกประเด็นหนึ่งคุณสามารถใช้การกำหนดค่าชนิดหนึ่งกับแอตทริบิวต์ของคุณผ่าน AttibuteUsage

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

เมื่อคุณตกแต่งคลาสแอ็ตทริบิวต์ด้วย AttributeUsage คุณสามารถบอกกับคอมไพเลอร์ c # ว่าฉันจะใช้แอ็ตทริบิวต์นี้: ฉันจะใช้สิ่งนี้กับคลาสแอสเซมบลีในคุณสมบัติหรือบน ... และแอ็ตทริบิวต์ของฉันได้รับอนุญาตให้ใช้ หลายครั้งกับเป้าหมายที่กำหนด (คลาสแอสเซมบลีคุณสมบัติ ... ) หรือไม่!

หลังจากคำจำกัดความเกี่ยวกับแอตทริบิวต์นี้ฉันจะแสดงตัวอย่างสมมติว่าเราต้องการกำหนดบทเรียนใหม่ในมหาวิทยาลัยและเราต้องการอนุญาตให้เฉพาะผู้ดูแลระบบและปริญญาโทในมหาวิทยาลัยของเรากำหนดบทเรียนใหม่ได้ไหม

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


}

ในโลกแห่งการเขียนโปรแกรมบางทีเราอาจจะไม่ใช้แนวทางนี้ในการใช้คุณลักษณะและฉันก็พูดแบบนี้เพราะประเด็นทางการศึกษาในการใช้แอตทริบิวต์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.