属性类如何工作?

我的search不断发现只有向导才能解释如何使用和应用属性的类。 我想学习如何创build我自己的属性类和他们如何工作的机制。

属性类是如何实例化的? 当它们被应用到的类被实例化时,它们是否被实例化? 是一个实例化每个类实例化,它是适用于? 例如,如果我将SerializableAttribute类应用于MyData类,并且实例化了5个MyData实例,那么会在后台创build5个SerializbleAttribute类的实例吗? 还是在它们之间共享一个实例?

属性类实例如何访问它们关联的类? 一个SerializableAttribute类如何访问它所应用的类,以便它可以序列化它的数据? 它是否有某种SerializableAttribute.ThisIsTheInstanceIAmAppliedTo属性? :)或者它的工作方向相反,每当我序列化的东西,Serialize函数我传递MyClass实例将reflection性通过属性,并findSerialiableAttribute实例?

我以前在日常工作中没有使用属性,但是我已经阅读了它们。 我也做了一些testing,来备份我要说的东西。 如果我在任何地方错了 – 随时告诉我这个:)

从我所知,属性并不像普通的类一样。 当你创build一个应用的对象时,它们不是实例化的,而不是一个静态实例,而不是每个对象的实例。 他们也不能访问他们申请的class级。

相反,他们的行为像类的属性(属性?:P)。 不像.NET类的属性 ,更像是“玻璃的一种属性就是透明度”的一种属性。 你可以通过reflection来检查哪些属性应用于某个类,然后对其进行相应的处理。 它们本质上是连接到类定义的元数据,而不是该types的对象。

你可以尝试获取一个类,方法,属性等等的属性列表。当你得到这些属性的列表 – 这是他们将被实例化。 然后你可以对这些属性中的数据进行操作。

例如Linq表,属性在它们上面定义了它们引用的表/列。 但是这些类不使用这些属性。 相反,DataContext会在将linqexpression式树转换为SQL代码时检查这些对象的属性。

现在来看一些真实的例子..我已经在LinqPad中运行了这些,所以不要担心奇怪的Dump()方法。 我已经用Console.WriteLinereplace了它,以使代码更容易理解不知道它的人:)

void Main() { Console.WriteLine("before class constructor"); var test = new TestClass(); Console.WriteLine("after class constructor"); var attrs = Attribute.GetCustomAttributes(test.GetType()).Dump(); foreach(var attr in attrs) if (attr is TestClassAttribute) Console.WriteLine(attr.ToString()); } public class TestClassAttribute : Attribute { public TestClassAttribute() { DefaultDescription = "hello"; Console.WriteLine("I am here. I'm the attribute constructor!"); } public String CustomDescription {get;set;} public String DefaultDescription{get;set;} public override String ToString() { return String.Format("Custom: {0}; Default: {1}", CustomDescription, DefaultDescription); } } [Serializable] [TestClass(CustomDescription="custm")] public class TestClass { public int Foo {get;set;} } 

该方法的控制台结果是:

 before class constructor after class constructor I am here. I'm the attribute constructor! Custom: custm; Default: hello 

Attribute.GetCustomAttributes(test.GetType())返回这个数组:(该表显示了所有条目的所有可用列。所以不,Serializable属性没有这些属性:)) LinqPad属性数组

还有什么问题吗? 随意问!

UPD:我看到你问一个问题:为什么要使用它们? 作为一个例子,我会告诉你关于XML-RPC.NET库。 您将创build您的XML-RPC服务类,并使用代表xml-rpc方法的方法。 现在最主要的是:在XmlRpc中,方法名可以有一些特殊的字符,比如点。 所以,你可以有一个flexlabs.ProcessTask()xml rpc方法。

你可以这样定义这个类:

 [XmlRpcMethod("flexlabs.ProcessTask")] public int ProcessTask_MyCustomName_BecauseILikeIt(); 

这允许我以我喜欢的方式命名该方法,同时仍然使用公有名称。

属性本质上是元数据,可以附加到你的代码的各个部分。 这个元数据然后可以interogate和影响某些操作的行为。

属性几乎可以应用于代码的每个方面。 例如,可以在组件级别关联属性,如AssemblyVersion和AssemblyFileVersion属性,这些属性pipe理与程序集关联的版本号。

 [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] 

那么Serializable属性例如可以被应用于一个types声明来标记该types为支持序列化。 实际上,这个属性在CLR中有特殊的含义,实际上作为一个特殊的指令直接存储在IL中的types中,这个属性被优化为可以被更高效地处理的位标志,这种性质被称为伪定制属性。

还有其他的属性可以应用于方法,属性,字段,枚举,返回值等。通过查看这个链接http://msdn.microsoft.com/zh-cn/ -us /库/ system.attributetargets(VS.90)的.aspx

除此之外,您可以定义自己的自定义属性,然后将其应用于您的属性适用的适用目标。 然后在运行时,您的代码可以反映自定义属性中包含的值并采取适当的操作。

对于一个相当天真的例子,这仅仅是为了举例:)你可能想写一个持久化引擎,它会自动将类映射到数据库中的表,并将类的属性映射到表列。 您可以从定义两个自定义属性开始

 TableMappingAttribute ColumnMappingAttribute 

然后,您可以将其应用于您的课程,例如我们有一个Person类

 [TableMapping("People")] public class Person { [ColumnMapping("fname")] public string FirstName {get; set;} [ColumnMapping("lname")] public string LastName {get; set;} } 

编译时,除了编译器发出由自定义属性定义的附加元数据的事实之外,其他一些事情都不会受到影响。 但是,现在您可以编写一个PersistanceManager,它可以dynamic检查Person类实例的属性,并将数据插入到People表中,将FirstName属性中的数据映射到fname列,将LastName属性映射到lname列。

至于你关于属性实例的问题,属性的实例不是为你的类的每个实例创build的。 People的所有实例将共享TableMappingAttribute和ColumnMappingAttributes的同一个实例。 实际上,属性实例只有在第一次实际查询属性时才会创build。

是的,他们是用你给它的参数实例化的。

该属性不“访问”类。 该属性附加到reflection数据中类的属性列表。

 [Serializable] public class MyFancyClass { ... } // Somewhere Else: public void function() { Type t = typeof(MyFancyClass); var attributes = t.GetCustomAttributes(true); if (attributes.Count(p => p is SerializableAttribute) > 0) { // This class is serializable, let's do something with it! } } 

认为属性是附属于类或方法定义(embedded在程序集元数据中)的post-its。

然后你可以有一个处理器/转轮/检查器模块,通过reflection来接受这些types,寻找这些后处理,并以不同的方式处理它们。 这被称为声明式编程。 你声明了一些行为,而不是在types中为它们编写代码。

  • types上的可序列化属性声明它是构build为序列化的。 然后,XmlSerializer可以接受这个类的一个对象,并做必要的事情。 您需要使用正确的post-its标记需要序列化/隐藏的方法。
  • 另一个例子是NUnit。 NUnit运行器查看目标程序集中定义的所有类的[TestFixture]属性,以确定testing类。 然后查找标有[Test]属性的方法来标识testing,然后运行testing并显示结果。

您可能需要在MSDN上完成本教程,其中大部分问题都会在最后回答一个示例。 虽然他们可以提取一种称为Audit(Type anyType); 而不是重复该代码。 这个例子通过检查属性来“打印信息”,但是你可以用同样的方法做任何事情。

如果您仔细看看这个可下载的开源代码LINQ to Active Directory(CodePlex) ,可能会发现Bart De Smet编写的所有属性类定义的Attributes.cs文件的机制。 我在那里学到了属性。

简而言之,您可以专注于Attribute类并为您的需求编写一些专门的属性。

 public class MyOwnAttributeClass : Attribute { public MyOwnAttributeClass() { } public MyOwnAttributeClass(string myName) { MyName = myName; } public string MyName { get; set; } } 

然后,您可以在MyOwnAttributeClass变得有用的地方使用它。 它可能是通过类定义或属性定义。

 [MyOwnAttributeClass("MyCustomerName")] public class Customer { [MyOwnAttributeClass("MyCustomerNameProperty")] public string CustomerName { get; set; } } 

那么,你可以像这样反思:

 Attribute[] attributes = typeof(Customer).GetCustomAttribute(typeof(MyOwnAttributeClass)); 

考虑到放在方括号之间的属性总是属性的构造函数。 所以,如果你想有一个参数化的属性,你需要编码你的构造函数。

此代码按原样提供,可能无法编译。 它的目的是让你知道它是如何工作的。

事实上,你通常希望为一个类有一个不同的属性类。

希望这可以帮助!

没有多less时间给你一个更完整的答案,但是你可以find已经应用到一个值使用reflection的属性。 至于创build它们,你从属性类inheritance,并从那里工作 – 你提供一个属性的值传递给属性类的构造函数。

已经有一段时间了,你也许可以告诉…

马丁