zoukankan      html  css  js  c++  java
  • 特性和属性

    【问】

    1、特定就是属性吗?

    2、以下结果输出什么?

    [C#]

    namespace Anytao.net
    {
    class MainClass
    {
    static void Main()
    {
    MainClass aa = new MainClass();
    aa.CannotRun();
    Console.ReadKey();
    }

    [Test("参数")]
    public void CannotRun()
    {
    Console.WriteLine("执行CannotRun()方法");
    }

    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,Inherited = true)]
    public class TestAttribute : System.Attribute
    {
    public TestAttribute(string message)
    {
    Console.WriteLine(message);
    }
    }
    }
    }

     [VB.NET]

    Namespace Anytao.net
    Class MainClass
    Public Shared Sub Main()
    Dim aa As New MainClass()
    aa.CannotRun()
    Console.ReadKey()
    End Sub

    <Test("参数")> _
    Public Sub CannotRun()
    Console.WriteLine("执行CannotRun()方法")
    End Sub

    <AttributeUsage(AttributeTargets.[Class] Or AttributeTargets.Method, Inherited := True)> _
    Public Class TestAttribute
    Inherits System.Attribute

    Public Sub New(message As String)
    Console.WriteLine(message)
    End Sub
    End Class
    End Class
    End Namespace

    【错误回答】

    既然“特性”和“属性”都翻译成Attribute,那么应该是一样的。

    输出message变量中的内容。

    【正解】

    初学者往往在看了一些书籍,或者是MSDN等地方的资料往往会把“特性”和“属性”划等号,其实两者完全不一样。“属性”是针对类变量而言,因为OOP设计原则告诉我们不该把私有变量对外公开,让外者直接访问;因此微软使用了“属性”这个东西来封装私有变量,对外进行交互。其实“属性”本质上也是普通的方法(成对的getset)。只不过这个方法相对其它而言做得事情较为简单——只负责读取对应私有变量值作为输出,或者是接受数据并且给对应的私有变量赋值。同时,使用“属性”还有一个好处在于可以在赋值的时候先对要赋值数值进行验证,以确保其是否符合输入的条件等。比如入幼稚园年龄规定在3岁到6岁,那么你就可以这样做:

    [C#]

    public class Kid
    {
    private int _age=0;

    public int Age
    {
    get {return _age;}
    set
    {
    if (_age<3 || _age>6)

    {
    throw new Exception("无法入园,年龄不符合!");
    }
    _age=value;
    }
    }
    }

    [VB.NET]

    Public Class Kid
    Private _age As Integer = 0

    Public Property Age() As Integer
    Get
    Return _age
    End Get
    Set
    If _age < 3 OrElse _age > 6 Then
    Throw New Exception("无法入园,年龄不符合!")
    End If
    _age = value
    End Set
    End Property
    End Class

    相对属性而言,“特性”是一种 “附加属性”,用于表示某个类,或者描述某个属性的额外信息。一个典型的例子是如果我们要使得一个类可以被“序列化”,我们应当在该类头部出加上“Serializable”特性——

    [C#]

    [Serializable]
    public class Model
    {
    }

    [VB.NET]

    <Serializable>
    Public class Model
    {
    }

    又如——如果我们定义了一个类库,后来更新这个类库,在这个新版本中为考虑兼容或是安全等因素,某些方法我们已经不提倡用户使用了,我们可以直接使用系统特性Obsolate来加以说明——

    [C#]

    public class Model
    {
    [Obsolete("不可使用,该方法过期。")]
    public void OldMethod()
    {
    //省略实现
    }
    }

    [VB.NET]

    Public Class Model
    <Obsolete("不可使用,该方法过期。")> _
    Public Sub OldMethod()
    '省略实现
    End Sub
    End Class

    明显地,如果你在主函数中使用了OldMethod,将会出现一个警告,同时编译器提示框也会告知此方法已经过期了。

    第二问是一个自定义特性,其中该特性有一个构造函数,输出的话允许人为自定义。通常而言,一个类被“new”了(在为该类创建新实例的时候),应该会自动调用其构造函数;但是“特性类”不然——因为“特性”只是作为一种“附加属性”存在,是对原有类或者其中的某些方法、属性等的补充描述,所以不会被直接运行。

    【总结】

    1)“属性”是针对“私有变量”而言,提供对外访问的“接口模式”。

    2)“特性”是对于类或者其内部方法、属性的补充描述,不属于“类”的一部分,所以不会和“类”一起运行。除非通过反射机制“激活”该类的特性并且使用代码运行某些特殊的功能。所以“特性”仅仅起到一个标识符的作用。

    【拓展】

    一、“特性”的定义和说明:

    我们知道了“特性”不是“类”的一部分,是独立于“类”单独存在的一种特殊的标识符。除了系统提供的“Serializable”和“Obsolate”等一些类,我们是否可以自定义特性呢?答案当然 是肯定的,第二问其实正是考察了这一点,现在我们就深入下去看看关于自定义特性的一些必要知识。

    从基本的特性定义(如第二问)我们可以知道“特性”的本质是一个继承Attribute的类,也可以有构造函数等(这些特性和一般类基本没有很大差别)。不过在这个自定义特性上有另一个特性“AttributeUsage——这个特性中文直面翻译就是“特性使用”,主要用于限定该特性的“使用范围”、“是否可以被继承”和“是否允许多次出现”。下面逐一介绍这些内容:

    1、使用范围:“AttributeTargets”是一个枚举类型,指明了该自定义特性可以被“放到”哪个地方起作用(比如像“Serializable”只能作用于类上,Obsolate只能作用于方法上等)。

    比如第二问中的那个TestAttribute只能用于“类”上,你用于类中的某个方法或者属性将会导致一个编译错误。

    2、多次出现(AllowMultiple):说明这个特性是否可以“重复”地多次。一般地,多个特性作用于某一个地方的写法有两种——

    2.1) [特性1,特性2,……,特性N]

                类(方法、属性)……

    2.2) [特性1]

                [特性2]

         ……………………

               [特性N]

              类(方法、属性)……

    如果这个值是false(默认也是false),那么就意味着作用于某个特定的类、属性或者方法等上时,就会引发一个Duplicate (重复定义)特性的错误。

    3、是否允许继承(AllowInherit):如果这个特性是作用于某些父类上,当有一个子类继承它的时候,子类是否可以获取与父类同样的特性。

    这里需要强调一点——“特性”的“继承性”和“是否允许重复”无关。这是因为有些人可能会问——如果父类和子类都有同一个特性分别作用在他们上边,那么会出现什么情况呢?考察下列例子:

    [C#]

    [AttributeUsage(AttributeTargets.Class,Inherited=true,AllowMultiple=false)]
    class RangeAttribute:Attribute
    {
    }

    [Range]
    public class Model
    {
    }

    [Range]
    public class S:Model
    {
    }

    [VB.NET]

    <AttributeUsage(AttributeTargets.[Class], Inherited := True, AllowMultiple := False)> _
    Class RangeAttribute
    Inherits Attribute
    End Class

    <Range> _
    Public Class Model
    End Class

    <Range> _
    Public Class S
    Inherits Model
    End Class

    或许你会说——因为S继承于Model类,又因为Model上的“Range”特性被继承了,导致了S类有两个“Range”了?其实不然——如果AllowMultiple=false,相当于是“子类把父类的特性给屏蔽了”,反之,如果AllowMultiple=true,子类保留其自身和父类继承下的特性(子类归子类的Range,父类是父类的,两者毫无关系;而且是先“子类”后“父类”地方式调用)。下面给出严格证明:

    I) 给出一个已经定义好的特性:

    [C#]

    [AttributeUsage(AttributeTargets.Class,Inherited=true,AllowMultiple=false)]
    class RangeAttribute:Attribute
    {
    public RangeAttribute(string s)
    {
    Console.WriteLine(s);
    }
    }

    [VB.NET]

    <AttributeUsage(AttributeTargets.[Class], Inherited := True, AllowMultiple := False)> _
    Class RangeAttribute
    Inherits Attribute

    Public Sub New(s As String)
    Console.WriteLine(s)
    End Sub
    End Class

    II) 定义一个Model和S类(其中S是Model的子类),套用此属性:

    [C#]

    [Range("父类")]
    public class Model
    {
    }

    [Range("子类")]
    public class S:Model
    {
    }

    [VB.NET]

    <Range("父类")> _
    Public Class Model
    End Class

    <Range("子类")> _
    Public Class S
    Inherits Model
    End Class

    3)最后在主函数中这样反射调用:

    [C#]

    foreach (var item in typeof(S).GetCustomAttributes(true)) ;

    [VB.NET]

    For Each item As var In GetType(S).GetCustomAttributes(True) Next

    现在AllowMultiple=false,那么运行结果你会发现只输出“子类”(说明父类的那个“特性”完全被“子类”重写了,不会被调用)。如果改成true,则输出“子类”、“父类”两项。注意“GetCustomAttributes”方法,该方法必须至少接受一个参数以便指定是否获取从父类继承的特性

    二、“特性”的应用:

    在“MVC”和“DynamicData”等多出场合,“模型验证”是“特性”表现的一个绝佳的舞台。今天我们在控制台中模拟类似类似功能:

    1、   先定义一个特性类,如下:

    [C#]

     [AttributeUsage(AttributeTargets.Property,Inherited=true,AllowMultiple=true)]
    class RangeAttribute:Attribute
    {
    public int MaxRange { get; set; }
    public int MinRange { get; set; }
    }

    [VB.NET]

    <AttributeUsage(AttributeTargets.[Property], Inherited := True, AllowMultiple := True)> _

    Class RangeAttribute

    Inherits Attribute

    Public Property MaxRange() As Integer
    Get
    Return m_MaxRange
    End Get

    Set
    m_MaxRange = Value
    End Set
    End Property

    Private m_MaxRange As Integer

    Public Property MinRange() As Integer
    Get
    Return m_MinRange
    End Get

    Set
    m_MinRange = Value
    End Set
    End Property

    Private m_MinRange As Integer
    End Class

    2、   编写一个通用验证方法,通过反射注意判定类的公共属性是否具备加载了特定的“Rage”特性,如果具备了,然后检测里边的数据是否在Max和Min的范围之内:

    [C#]

    static void ModelValidate(this Model m)
    {
    foreach (PropertyInfo item in m.GetType().GetProperties())
    {
    foreach (object attr in item.GetCustomAttributes(false))
    {
    if (attr is RangeAttribute)
    {
    //获取Max和Min的内容
    RangeAttribute r = (RangeAttribute)attr;
    int n = (int)item.GetValue(m, null);
    if (n < r.MinRange || n > r.MaxRange)
    {
    throw new Exception("数组越界!");
    }
    }
    }
    }
    }

    [VB.NET]

    <System.Runtime.CompilerServices.Extension> _
    Private Shared Sub ModelValidate(m As Model)

    For Each item As PropertyInfo In m.[GetType]().GetProperties()
    For Each attr As Object In item.GetCustomAttributes(False)
    If TypeOf attr Is RangeAttribute Then
    '获取Max和Min的内容
    Dim r As RangeAttribute = DirectCast(attr, RangeAttribute)
    Dim n As Integer = CInt(item.GetValue(m, Nothing))

    If n < r.MinRange OrElse n > r.MaxRange Then
    Throw New Exception("数组越界!")
    End If
    End If
    Next
    Next

    End Sub

    3、   最后主函数中这样调用该静态方法:

    [C#]

    Model m = new Model();
    m.Age = 31;
    ModelValidate(m);

    [VB.NET]

    Dim m As New Model()
    m.Age = 31
    ModelValidate(m)

    4、 运行结果,你就明白它抛出异常了。

  • 相关阅读:
    .NET基础 一步步 一幕幕[循环、逻辑语句块]
    .NET 基础 一步步 一幕幕 [注释、命名规则、访问修饰符、数据类型、常量、变量]
    .NET 基础 一步步 一幕幕 [.NET 系列预热]
    .NET 基础 一步步 一幕幕 [前言]
    前端面试题五
    前端面试题四
    ActiveMQ 的客户端选项
    ActiveMq 高级特性的使用
    企业环境中部署 ActiveMQ
    在其他平台上使用 ActiveMQ
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2248527.html
Copyright © 2011-2022 走看看