【发布时间】:2011-06-20 06:06:05
【问题描述】:
谁能向我解释一个带有代码的自定义属性的非常基本的示例?
【问题讨论】:
标签: c# custom-attributes
谁能向我解释一个带有代码的自定义属性的非常基本的示例?
【问题讨论】:
标签: c# custom-attributes
您首先编写一个派生自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
关于属性的重要知识:
【讨论】:
var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
虽然创建自定义Attribute 的代码相当简单,但了解什么是属性非常重要:
属性是编译到程序中的元数据。属性本身不会向类、属性或模块添加任何功能——只是数据。但是,使用反射,可以利用这些属性来创建功能。
因此,例如,让我们看一下来自 Microsoft 的 Enterprise Library 的 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; }
从上面的 sn-p 中,人们可能会猜测代码将始终根据验证器的规则进行验证,无论何时更改(在示例中,至少有 8 个字符,最多有 8 个字符)。但事实是,属性什么也没做。如前所述,它仅向属性添加元数据。
然而,企业库有一个Validation.Validate 方法可以查看您的对象,并且对于每个属性,它会检查内容是否违反了属性通知的规则。
所以,这就是您应该如何考虑属性 - 一种将数据添加到代码中的方法,这些数据可能稍后会被其他方法/类/等使用。
【讨论】:
利用/复制Darin Dimitrov's great response,这是访问属性而不是类的自定义属性的方法:
被装饰的属性[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 的each 属性上的此自定义属性,以及:
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()
简短的回答是在 c# 中创建一个属性,你只需要从 Attribute 类继承它,就这个:)
但是这里我要详细解释一下属性:
基本上,属性是类,我们可以使用它们将我们的逻辑应用于程序集、类、方法、属性、字段……
在 .Net 中,Microsoft 提供了一些预定义的属性,例如 Obsolete 或 Validation Attributes,例如 ([Required], [StringLength(100)], [Range(0, 999.99)]),我们还有一些属性,例如 ActionFilters asp.net 对于将我们想要的逻辑应用到我们的代码非常有用(如果您有兴趣学习它,请阅读this关于动作过滤器的文章)
另外一点,您可以通过 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();
}
}
}
在现实的编程世界中,我们可能不会使用这种方法来使用属性,我之所以这么说是因为它在使用属性方面具有教育意义
【讨论】: