代码之家  ›  专栏  ›  技术社区  ›  AaronLS

属性类如何工作?

  •  52
  • AaronLS  · 技术社区  · 15 年前

    我的搜索一直只找到解释如何使用和应用属性到类的指南。我想学习如何创建自己的属性类以及它们如何工作的机制。

    属性类是如何实例化的? 当应用到的类被实例化时,它们是否被实例化?是否为应用它的每个类实例化了一个实例?例如,如果我将serializibleattribute类应用于mydata类,并实例化5个mydata实例,那么在后台创建的serializibleattribute类会有5个实例吗?或者他们之间只有一个共享的实例?

    属性类实例如何访问与其关联的类? 序列化属性类如何访问它所应用的类,以便可以序列化它的数据?它是否具有某种可序列化的属性。此属性是否为InstanceIAmappliedto属性?:)或者它的工作方向是相反的,当我序列化某个东西时,我传递MyClass实例的序列化函数会反射性地遍历属性并找到SerialiableAttribute实例?

    6 回复  |  直到 15 年前
        1
  •  35
  •   Community CDub    8 年前

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

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

    相反,它们的行为就像属性(属性?:p)类的。不象.NET类 properties,更像“one properties of glass is transparency”类的属性。您可以通过反射检查哪些属性应用于类,然后相应地对其进行操作。它们本质上是附加到类定义的元数据,而不是该类型的对象。

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

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

    下面是一些真实的例子……我在 linqpad 中运行了这些,所以不要担心奇怪的dump()方法。我用console.writeline替换了它,使不了解它的人更容易理解代码:)

    void main())
    {
    console.writeline(“类构造函数之前”);
    var test=new testclass();
    console.writeline(“类构造函数之后”);
    
    var attrs=attribute.getCustomAttributes(test.getType()).dump();
    foreach(attrs中的var attr)
    if(attr是testclassattribute)
    console.writeline(attr.toString());
    }
    
    公共类testclassattribute:属性
    {
    公共testclassattribute()。
    {
    defaultdescription=“你好”;
    console.writeline(“我在这里。我是属性构造函数!“);
    }
    公共字符串customDescription get;set;
    公共字符串默认描述get;set;
    
    公共重写字符串ToString())
    {
    返回string.format(“自定义:0;默认:1”,自定义描述,默认描述);
    }
    }
    
    [可序列化]
    [测试类(customDescription=“custm”)]
    公共类TestClass
    {
    public int foo_get;set;
    }
    < /代码> 
    
    

    此方法的控制台结果是:

    类构造函数之前的代码 类后构造函数 我在这里。我是属性构造函数! 自定义:custm;默认:hello < /代码>

    attribute.getCustomAttributes(test.getType()).返回这个数组: (该表显示所有条目的所有可用列..所以不,可序列化属性没有这些属性:)

    还有问题吗?请随便问!

    upd: 我看到你问了一个问题:为什么要用它们? 作为一个例子,我将介绍XML-RPC.NET库。 使用将表示XML RPC方法的方法创建XML-RPC服务类。现在主要的事情是:在xmlrpc中,方法名可以有一些特殊的字符,比如点。因此,您可以使用flexlabs.processtask()xml rpc方法。

    您可以定义这个类如下:

    [xmlRpcMethod(“flexLabs.processTask”)]
    public int processtask_mycustomname_becauselikeit();
    < /代码> 
    
    

    这允许我以我喜欢的方式命名方法,同时仍然使用公共名称,因为它必须是这样的。

    我在这里说什么。如果我在任何地方都错了-请随意告诉我这件事:)

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

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

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

    例如,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())返回此数组: (该表显示所有条目的所有可用列..所以不,可序列化属性没有这些属性:) LinqPad Attributes Array

    还有问题吗?请随便问!

    UPD: 我看到你问了一个问题:为什么要用它们? 作为一个例子,我将介绍XML-RPC.NET库。 使用将表示XML RPC方法的方法创建XML-RPC服务类。现在主要的事情是:在xmlrpc中,方法名可以有一些特殊的字符,比如点。因此,您可以使用flexlabs.processtask()xml rpc方法。

    您可以定义这个类如下:

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

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

        2
  •  16
  •   Chris Taylor    15 年前

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

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

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

    然后,可以将可序列化属性(例如)应用于类型声明,以将该类型标记为支持序列化。事实上,这个属性在clr中有特殊的含义,并且实际上是作为一个特殊的指令直接存储在il中的类型上,它被优化为存储为一个位标志,可以更有效地进行处理,在这种性质上有一些属性,称为伪自定义属性。

    还有其他属性可以应用于方法、属性、字段、枚举、返回值等。您可以通过查看此链接了解属性可以应用于的可能目标。 http://msdn.microsoft.com/en-us/library/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;}
    }
    

    当这进行编译时,除了编译器发出由自定义属性定义的附加元数据之外,其他几乎不受影响。但是,现在可以编写PersistenceManager,它可以动态地检查Person类实例的属性,并将数据插入到People表中,将FirstName属性中的数据映射到fname列,将Lastname属性映射到lname列。

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

        3
  •  6
  •   Aren    15 年前

    是的,它们是用您提供的参数实例化的。

    属性不“访问”类。该属性附加到反射数据中类“/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!
    
       }     
    }
    
        4
  •  6
  •   Gishu    15 年前

    思考属性是发布附加到类或方法定义(嵌入到程序集元数据中)的属性。

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

    • 类型的可序列化属性声明它是为序列化而生成的。然后,XmlSerializer可以接受此类的对象并执行必要的操作。您可以用正确的post-its标记需要序列化/隐藏的方法。
    • 另一个例子是修女。nunit运行程序查看目标程序集中定义的所有类的[TestFixture]属性,以标识测试类。然后,它查找用[test]属性标记的方法来标识测试,然后运行并显示结果。

    你可能想跑过去 this tutorial at MSDN 最后给出一个例子来回答你的大部分问题。尽管他们可以提取一个名为 Audit(Type anyType); 而不是复制代码。示例“通过检查属性打印信息”。但你可以用同样的方式做任何事情。

        5
  •  2
  •   Will Marcouiller    15 年前

    如果你仔细看看这个可下载的开源代码 LINQ to Active Directory (CodePlex) 您可能会发现attributes.cs文件的机制很有趣,bart de smet在该文件中编写了所有的attributes类定义。我在那里学到了属性。

    简而言之,你可以专门研究 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));
    

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

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

    实际上,您通常希望类的属性类与属性的属性类不同。

    希望这有帮助!

        6
  •  1
  •   Martin Milan    15 年前

    没有太多时间给你一个更完整的答案,但是你可以通过反射找到已经应用到某个值的属性。至于创建它们,您从属性类继承并从中进行工作——您为属性提供的值将传递给属性类的构造函数。

    已经有一段时间了,正如你可能知道的…

    马丁