如何在C#中创建自定义属性


119

我已经尝试了很多次,但是仍然无法理解自定义属性的用法(我已经经历了很多链接)。

谁能给我解释一个带有代码的自定义属性的非常基本的示例吗?

Answers:


96

尽管创建自定义属性的代码非常简单,但是了解什么是属性非常重要:

属性是编译到程序中的元数据。属性本身不会为类,属性或模块添加任何功能,而只是添加数据。但是,使用反射,可以利用这些属性来创建功能。

因此,例如,让我们来看一下Microsoft 企业库中的Validation Application Block。如果查看代码示例,您将看到:

    /// <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个字符)。但事实是,属性什么也没做。如前所述,它仅将元数据添加到属性。

但是,企业库提供了Validation.Validate一种可以查看您的对象的方法,对于每个属性,它都会检查内容是否违反该属性所通知的规则。

因此,这就是您应该考虑的属性-一种将数据添加到代码中的方式,以后可能会被其他方法/类/等使用。


我会特别喜欢这个答案吗?”还有一个问题,我可以在上述代码的set语句中添加相同的条件,以便它与属性有何不同,
slash shogdhe 2011年

1
@slash:您可以改写吗?我不太明白这个问题。
布鲁诺·布兰特

1
我认为斜线代表询问使用属性和将实际验证代码放入属性设置器之间的区别。答:虽然可以在设置器中编写代码来验证值,但是仅使用属性将无法进行验证。属性只是“元数据”。其他地方的另一个代码应该对您使用的属性感兴趣,读取它们并根据它们执行操作。一个典型的示例是验证库,如@BrunoBrant所述。
罗马

10
不知道为什么这是公认的答案。实际的问题(也在Google中建立了索引)是“如何在C#中创建自定义属性”。答案根本不涉及该主题。另一方面,第二个答案可以。
Drakestar

我认为第二个答案与问题更相关。
Mohammad Taherian

267

首先编写一个从Attribute派生的类:

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

有关属性的重要注意事项:

  • 属性是元数据。
  • 它们被烘烤到组装中 编译时这对如何设置它们的属性有非常严重的影响。仅接受常量(在编译时已知)值
  • 发挥自定义属性的意义和用途的唯一方法是使用 Reflection。因此,如果您在运行时不使用反射来获取它们并使用自定义属性装饰某些东西,则不会发生太多事情。
  • 创建属性的时间是不确定的。它们由CLR实例化,您绝对无法控制它。

3
我应该在哪个函数/类中的哪个位置/中使用反射来获取它?
Hasan A Yousef

@Hasan Yousef,例如在Entity Framework中,有一个“ Key”属性对框架说:此属性应视为主键。在创建ORM时,属性非常有用
Parsa

如何访问属性而不是类的自定义属性?
Canvas,

docs.microsoft.com/en-us/dotnet/standard/attributes/...只是为了完整性,这个MSDN网页总结得很好
巴里斯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()每个未知属性的方法
heyNow19年

0

简短的答案是在c#中创建属性,您只需要从Attribute类继承它,就这样:)

但是在这里,我将详细解释属性:

基本上,属性是类,我们可以使用它们将逻辑应用于程序集,类,方法,属性,字段,...

在.Net中,Microsoft提供了一些预定义的属性,例如“过时”或“验证属性”,例如([Required],[StringLength(100)],[Range(0,999.99)]),此外,我们在asp.net中也提供了诸如ActionFilters之类的属性这对于将我们期望的逻辑应用于代码非常有用(如果您热衷于学习有关动作过滤器的文章,请阅读文章)

另外一点,您可以通过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();
        }
    }


}

在编程的现实世界中,也许我们不使用这种方法来使用属性,我之所以这样说是因为它在使用属性方面具有教育意义

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.