CSharp特性详解

前言

CSharp的特性(Attribute)是比较难以理解的技术,写代码时通常都要求写注释,为了是让其他程序猿快速理解代码含义,但是注释是写给'人'看的,突发奇想下:能不能写出给C#编译器看的注释,比如在某些代码段上打上标记,让编译器看到标记后,做出不同的运行效果?其实…这就是特性。

1.Serializable特性分析

为什么Serializable特性作为小节1讲解呢?因为它是比较常见的特性,在网络对象进行传输时和数据库进行对象保存时,使用序列化特性后的类、结构体、枚举等等都可实现序列化操作的,SerializableAttribute仅是标记而已,它并不执行序列化动作。这样为何在C#中必须要使用它呢?而其他语言好像没有C#这种技术,接下来展示序列化的代码:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace TestPro
{
    [Serializable]
    public class Person
    {
        public int Age { get; set; }
        public int Sex { get; set; }
        public string Name { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Age = 18;
            person.Sex = 0;
            person.Name = "李洛克";
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream("PersonFile.bin", FileMode.Create,
            FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, person);
            stream.Close();
        }
    }
}

运行生成.dll和.exe以后,通过ildasm查看il代码分析下原因,以下截图就是Person类的。

exp1

只是多了serializable指令,而且比起其他特性,它的构造函数都没有执行({}红色区域是正常特性的构造函数执行位置),从il代码看不出什么情况,只能从功能去猜想序列化实现的原理,通常来说序列化某个自定义类时,都要程序猿去实现具体的序列化方法(比如C、Delphi等等低级语言),而C#使用formatter.Serialize(stream, person)即可,这是什么神仙接口呢?(没有具体告诉接口类里有那些属性、变量),但使用它可完成基本的序列化动作,其实C#是通过反射这种技术去完成序列化接口的,了解反射原理的程序猿可自定义实现通用的序列化接口,反射打个比方就是文件夹下查找某个文件,使用序列化特性可以提高反射性能(查找对应类和获取到它的属性、变量性能),下面给出简单示意图。

exp2

使用序列化特性标记以后可提高性能,不用遍历程序集所有类、结构体,所以使用C#接口进行序列化时需要使用它,不然就自定义序列化接口(比如Person1使用Person1Serialize,Person2使用Person2Serialize)。如果对序列化方式不满意,但不想自定义序列化类,希望通过C#接口实现序列化也是有办法的,Person继承ISerializable,实现GetObjectData即可,比如序列化时给名字添加前缀或不序列化名字,代码实现如下。

	[Serializable]
    public class Person : ISerializable
    {
        public int Age { get; set; }
        public int Sex { get; set; }
        public string Name { get; set; }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Age", Age);
            info.AddValue("Sex", Sex);
            info.AddValue("Name","火影忍者" + Name);
        }
    }

2.CSharp预定义特性

小节2在网上可以普遍查询到的资料,这里简单说明下情况,.Net框架提供三种预定义特性:

  • AttributeUsage
  • Conditional
  • Obsolete

预定义特性 AttributeUsage 描述了如何使用自定义特性类。它规定了特性可应用到的项目的类型。规定该特性的语法如下:

	[AttributeUsage(
	   validon,//AttributeTargets.Class...
	   AllowMultiple=allowmultiple,//该特性是否多用的
	   Inherited=inherited//该特性是否继承的
	)]

Conditional预定义特性标记了条件方法,其方法是否被执行依赖指定的预处理标识符是否被定义。具体代码如下:

#define DEBUG
using System;
using System.Diagnostics;
public class Debug
{
    [Conditional("DEBUG")]
    public static void Log(string msg)
    {
        Console.WriteLine(msg);
    }

    [Conditional("DEBUG")]
    public static void LogWarning(string msg)
    {
        Console.WriteLine("w:" + msg);
    }

    [Conditional("DEBUG")]
    public static void LogError(string msg)
    {
        Console.WriteLine("e:" + msg);
    }
}
class TestPro
{
    public static void Main()
    {
        Debug.Log("In Main function.");
        Console.ReadKey();
    }
}

Obsolete预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。具体代码如下:

    public class Math
    {
        [Obsolete("Don't use Cal, use gCal instead", true)]
        public static void Cal()
        {
            
        }

        public static void gCal()
        {
            
        }
    }

3.(AttributeUsage)自定义特性使用

开发经常遇到的情况,就是结构体、类需要稍微调整下,但不想改动过大,而且改动逻辑是偏向行为方面的改变,比如消息协议类之前都是在单个服务器上运行的,经过实战以后发现服务器压力过大了,公司领导准备让程序猿优化服务器性能,首先想到的就是分布式服务器设计思路,把服务器功能分开后部署到不同服务器机器上(处理Http请求的服务器、数据库操作的服务器、网关服务器、处理游戏战斗模块服务器),这样可以很大程度减轻服务器的压力(把拆分出的服务器还是部署到一个机器上,就和之前没有区别,可能还会增加本地服务器之间微小的通信消耗),所以需要大量重构代码,这时可使用自定义特性的奇淫技巧来解决这方面问题,修改过的伪代码如下:

using System;
namespace TestPro
{
    public enum AppType
    {
        Gate,
        Realm,
        Http,
        DB
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    public class MessageHandlerAttribute : Attribute
    {
        public AppType appType { get; }
        public Type selfType { get; }

        public MessageHandlerAttribute()
        {
        }

        public MessageHandlerAttribute(AppType appType)
        {
            this.appType = appType;
            this.selfType = this.GetType();
        }
    }

    [MessageHandler(AppType.Gate)]
    public class CG_LoginMessageHandler
    {
        public void RunTask()
        {
            Console.WriteLine("登陆网关服务器");
        }
    }

    [MessageHandler(AppType.DB)]
    public class CD_RegisterMessageHandler
    {
        public void RunTask()
        {
            Console.WriteLine("注册数据库服务器");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            CD_RegisterMessageHandler registerMessageHandler = new CD_RegisterMessageHandler();
            var messageAttr = (MessageHandlerAttribute)Attribute.GetCustomAttribute(
                registerMessageHandler.GetType(), typeof(MessageHandlerAttribute));
            Console.WriteLine(messageAttr.appType);
            Console.WriteLine(messageAttr.selfType);
        }
    }
}

可能稍微修改代码就适合以上需求,正常来说Main函数里应该有个死循环,然后取用消息队列里的消息句柄类,通过特性附加的信息进行不同的处理(比如把消息分拨都其他服务器),这里只获取、打印出附加的特性信息,概念图应该如下所示:

exp3

可能分布式服务器的概念图是这样的,如果不符合逻辑的话或者不够严谨的话(随便画的),希望各位不要太过较真,可以理解大概意思即可。

4.总结

常用的内建特性表格:

特性作用
JsonPropertyJsonConvert.SerializeObject指定json格式的键值名称
Serializable可序列化
NonSerialized不可序列化,可在类内部变量使用,表面序列化时过滤掉它
DLLImport非托管代码实现的
WebMethod被作为web服务器对外暴露的方法
Required必须存在的字段
MaxLength(100)限制字符串、数组长度
DebuggerStepThrough在代码打断点调试过程中,不进入该方法,加在不可能有错误的地方,方便调试
  • Attribute是什么?
    Attribute是一种可由用户自有定义的修饰符(Modifier),可以用来修饰各种需要被修饰的目标。可以对类、以及C#程序集中的成员进行进一步的描述。简单地说,Attribute就是一种“附着物”,就像牡蛎吸附在船底或礁石上一样。 这些附着物的作用是为它们附着体追加上额外的信息(这些信息保存在附着物的体内),通过Ildasm可以发现特性通常会被附带构造,Attribute是程序代码一部分,它不会被编译器丢弃,而且还会被编译器编译进程序集(Assembly)的元数据里(Metadata)里。在程序运行时,可以随时从元数据(.NET的元数据是指程序集中的命名空间、类、方法、属性等信息,这些信息是可以通过Reflection读取出来的)中获取出这些附加信息,并可以决策程序的运行效果。

  • Attribute与注释的区别
    注释是对程序源代码的说明,主要目的是给人看的,在程序被编译的时候会被编译器所丢弃,因此它丝毫不会影响到程序的执行。Attribute是给编译器看的,CSharp内建的特性可以达到很多有趣的效果,比如跳过调式、提示函数过期、编译开关效果等,自定义的特性也可以自行决定附加信息的用处。

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值