属性类如何工作?

时间:2010-04-20 15:55:49

标签: c# attributes

我的搜索只会提供解释如何使用和将属性应用于类的指南。我想学习如何创建自己的属性类以及它们如何工作的机制。

如何实例化属性类?是否在实例化应用它们的类时实例化它们?是否为实例化它应用于的每个类实例化?例如。如果我将SerializableAttribute类应用于MyData类,并实例化5个MyData实例,那么将在幕后创建5个SerializbleAttribute类实例吗?或者只有一个实例在所有实例之间共享?

属性类实例如何访问与它们关联的类? SerializableAttribute类如何访问它所应用的类,以便它可以序列化它的数据?它是否具有某种SerializableAttribute.ThisIsTheInstanceIAmAppliedTo属性? :)或者它是否反向工作,每当我序列化某些东西时,我传递MyClass实例的Serialize函数将反射性地通过属性并找到SerialiableAttribute实例?

6 个答案:

答案 0 :(得分:35)

我以前没有在日常工作中使用属性,但我已经读过它们了。 我也做了一些测试,以支持我在这里说的话。如果我在任何地方都错了 - 请随时告诉我:)

据我所知,属性不是常规类。当您创建应用它们的对象时,它们不会被实例化,而不是一个静态实例,而不是每个对象实例1。 他们也没有访问他们应用的类..

相反,它们的行为类似于属性(属性?:P)。不像.NET类属性,更像是“玻璃的一个属性是透明度”的属性。您可以检查哪些属性从反射应用于类,然后相应地对其进行操作。它们本质上是附加到类定义的元数据,而不是该类型的对象。

您可以尝试获取类,方法,属性等的属性列表。当您获得这些属性的列表时 - 这是它们将被实例化的位置。然后,您可以对这些属性中的数据进行操作。

E.g。 Linq表,属性上有属性,用于定义它们引用的表/列。但是这些类不使用这些属性。相反,DataContext将在将linq表达式树转换为SQL代码时检查这些对象的属性。

现在有一些真实的例子..我在LinqPad中运行了这些,所以不要担心奇怪的Dump()方法。我用Console.WriteLine替换它,使代码更容易理解为不了解它的人:)

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 Attributes Array

还有其他问题吗?随意问一下!

<强> UPD: 我见过你问过一个问题:为什么要用它们? 作为一个例子,我将告诉你XML-RPC.NET库。 您可以使用表示xml-rpc方法的方法创建XML-RPC服务类。现在最重要的是:在XmlRpc中,方法名称可以包含一些特殊字符,如点。因此,您可以使用flexlabs.ProcessTask()xml rpc方法。

您可以按如下方式定义此类:

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

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

答案 1 :(得分:16)

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

属性几乎可以应用于代码的每个方面。例如,属性可以在程序集级别关联,例如AssemblyVersion和AssemblyFileVersion属性,它们控制与程序集关联的版本号。

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

然后,可以将Serializable属性应用于类型声明,以将该类型标记为支持序列化。事实上,这个属性在CLR中具有特殊含义,实际上直接作为特殊指令存储在IL中的类型中,这被优化为存储为位标志,可以更有效地处理,有一些属性这种性质,称为伪自定义属性。

还可以将其他属性应用于方法,属性,字段,枚举,返回值等。通过查看此链接,您可以了解可以应用属性的可能目标 http://msdn.microsoft.com/en-us/library/system.attributetargets(VS.90).aspx

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

对于一个相当天真的例子,这只是为了举例:) 您可能希望编写一个持久性引擎,该引擎将自动将Classes映射到数据库中的表,并将Class的属性映射到表列。您可以从定义两个自定义属性开始

TableMappingAttribute
ColumnMappingAttribute

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

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

  [ColumnMapping("lname")]
  public string LastName {get; set;}
}

当编译时,除了编译器发出自定义属性定义的附加元数据这一事实外,其他几乎没有受到影响。但是,您现在可以编写一个PersistanceManager,它可以动态检查Person类实例的属性,并将数据插入People表,将FirstName属性中的数据映射到fname列,将LastName属性映射到lname列。 / p>

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

答案 2 :(得分:6)

是的,它们会使用您提供的参数进行实例化。

该属性不会“访问”该类。该属性附加到反射数据中的类'/ property的属性列表中。

[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!

   }     
}

答案 3 :(得分:6)

认为属性是post-it附加到类或方法定义(嵌入在程序集元数据中)。

然后你可以有一个处理器/跑步者/检查员模块通过反射来接受这些类型,查找这些类型并以不同的方式处理它们。这称为声明性编程。您声明了一些行为,而不是在类型中为它们编写代码。

  • 类型上的Serializable属性声明它是为了序列化而构建的。然后,XmlSerializer可以接受此类的对象并完成所需的操作。您可以使用正确的post-it标记需要序列化/隐藏的方法。
  • 另一个例子是NUnit。 NUnit运行器查看目标程序集中定义的所有类的[TestFixture]属性,以标识测试类。然后,它会查找标有[Test]属性的方法,以识别测试,然后运行并显示结果。

您可能希望运行this tutorial at MSDN,其中包含大部分问题以及最后的示例。虽然他们可以提取一个叫做的方法 Audit(Type anyType);而不是复制该代码。该示例通过检查属性来“打印信息”..但您可以以同样的方式执行任何操作。

答案 4 :(得分:2)

如果您注意这个可下载的开源代码LINQ to Active Directory (CodePlex),您可能会发现Barts 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));

请考虑放在方括号之间的属性始终是属性的构造函数。因此,如果您想拥有参数化属性,则需要对构造函数进行编码。

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

实际上,您通常希望为类创建一个不同于属性的属性类。

希望这有帮助!

答案 5 :(得分:1)

没有多少时间给您更全面的答案,但您可以使用Reflection找到已应用于值的属性。至于创建它们,您从属性类继承并从那里继续工作 - 并且您使用属性提供的值将传递给Attribute类的构造函数。

已经有一段时间了,你可能会说...

马丁