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

有人能给我解释一下自定义属性和代码的一个非常基本的例子吗?

156559 次浏览

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

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
}

您可以使用 属性用法属性限制这个自定义属性可以应用到的目标类型:

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

关于属性需要知道的重要事情:

  • 属性是元数据。
  • 它们被加入到 编译时的程序集中,这对于如何设置它们的属性具有非常重要的意义。只接受常量(在编译时已知)值
  • 使自定义属性具有任何意义和用法的唯一方法是使用 反射。因此,如果您不在运行时使用反射来获取它们,并使用自定义属性来装饰某些东西,那么不要期望会发生什么。
  • 属性的创建时间是不确定的。它们是由 CLR 实例化的,您完全无法控制它。

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

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

例如,让我们看看来自微软 企业图书馆验证应用程序块,如果你看一个代码示例,你会看到:

    /// <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 什么也不做; 如前所述,它只是向属性添加元数据。

但是,Enterprise Library 有一个 Validation.Validate方法,它将查看您的对象,并且对于每个属性,它将检查内容是否违反了该属性通知的规则。

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

利用/复制 达林 · 迪米特洛夫的伟大回应,这是如何访问属性而不是类的自定义属性:

修饰的属性[类 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
}
}

多亏了你,达林!

简单来说,就是在 c # 中创建一个属性,你只需要从 Attribute 类继承它,就是这样:)

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

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

进去。微软已经提供了一些预定义的属性,比如过时属性或验证属性,比如([必需] ,[ StringLlength (100)] ,[ Range (0,999.99)]) ,我们还有一些属性,比如 asp.Net 中的 ActionFilters,它们对于将我们所需的逻辑应用到我们的代码中非常有用(如果你热衷于学习的话,请阅读 这个关于操作过滤器的文章)

另一方面,您可以通过 AttibuteUse 对属性应用某种配置。

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

当你用 AttributeUse 修饰属性类时,你可以告诉 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();
}
}




}

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