WPF中使用Attribute类
背景
Attribute类是定义在System命名空间下面的一个抽象类,我们需要在很多的场合下面使用到它,除了.Net 框架中经常用到的一些定义好的特性外,我们也可以继承Attribute这个基类来构建我们自己的特性类,本篇文章我将从两个方面来介绍,一个是WPF中开发常用到的一些特性以及如何在项目中进行自定义自己的特性。
一 框架中常用的一些特性
1.1 DebuggerDisplay
我们来看看这个定义在System.Diagnostics命名空间下面的这个特性,这个特性的解释是: 确定类或字段在调试器变量窗口中的显示方式。这个在调试代码的时候还是非常有用的,这个到底该怎么使用,我们先来看一段示例代码。
[DebuggerDisplay("RecipeName={Name},Steps={Steps}")] public class RecipeInfo { public string Name { get; private set; } public int Priority { get; set; } public List<RecipeStep> Steps { get; set; } public RecipeInfo(string recipeName) { this.Name = recipeName; Steps = new List<RecipeStep>(); } }
我们在RecipeInfo这个类上面加DebuggerDisplay属性的时候,当我们调试代码的时候,当我们的鼠标移动到这个对象上面的时候会按照我们定义的格式来显示我们的对象,下面通过一个截图来进行说明。
图一 使用DebuggerDisplay特性
当然这个特性可以用在类上面当然也是可以用在属性上面,这个原理上面都是一样的,其内部都是通过反射的方式去动态获取的,这个原理我们必须清楚。
1.2 DllImport
这个肯定不需要做过多的解释,这个是定义在System.Runtime.InteropServices下面的一个特性,很多时候C#代码在引用Win32 API或者是其它库中使用的API的时候都需要通过这个来进行引用,这个来看一个具体的例子
public static class ConsoleManager { private const string Kernel32_DllName = "kernel32.dll"; [DllImport(Kernel32_DllName)] private static extern bool AllocConsole(); [DllImport(Kernel32_DllName)] private static extern bool FreeConsole(); [DllImport(Kernel32_DllName)] private static extern IntPtr GetConsoleWindow(); [DllImport(Kernel32_DllName)] private static extern int GetConsoleOutputCP(); public static bool HasConsole { get { return GetConsoleWindow() != IntPtr.Zero; } } }
1.3 Obsolete
这个也用到很多,这个特性表示标记不再使用的程序元素, 此类不能被继承,可以定义在临时不想删除代码,但又需要做一些区别的程序元素中,下面以一个接口类型为例来说明。
[Obsolete("Use abstract class BuilderRule instead.")] internal interface IBuilderRule { void UpdateScheduleQueueByLinker(string waferName, IList<ScheduleQueue> queues, TimeSpan minStartTime, StationLinker linker); }
1.4 WCF服务中定义特性
也许WCF中是使用特性最多,定义的各种契约,各种约束均使用到各种特性,例如:DataContract、OperationContract、DataMember等等一系列的各种特性。
1.5 序列化过程中定义Serializable特性
很多时候使用XML序列化或Json序列化的时候需要在类上面添加此特性用来表示当前类是可以被序列化的。
总结:
这样的例子在整个WPF开发的时候是非常常见的,这样的例子举也举不玩,这里仅列举了一些常用的例子来做一个总结
二 自定义自己的特性
在我们的项目中有时经常会标识一些类的特性,在下面我们将简短的方式来介绍如何构建自定义的Attribute类。
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace DLPVideoApp.DLPCtrl { // 摘要: // 定义宿主程序的识别码。 [ComVisible(true)] [AttributeUsage(AttributeTargets.Class, Inherited = true)] public sealed class MyClassAttribute : Attribute { private string _identify = ""; // 摘要: // 初始化 System.Reflection.AssemblyTitleAttribute 类的新实例。 // // 参数: // identifyNumber: // 识别码。 public MyClassAttribute(string identifyType) { _identify = identifyType; } public string IdentifyType { get { return _identify; } } } }
在定义完一个继承自Attribute的基类之后,接下来就是如何去应用我们自定义的MyClassAttribute类了,其实也非常简单,我们只需要在要标明类的上面添加下面的内容即可:[MyClassAttribute("DLPVideoApp.DLPCtrl.DLPWin")](这里仅仅是举出一个案例)
接下来就是如何去应用该Attribute了,下面通过反射的方式来获取程序集中所有的类相关信息,然后再来匹配名称为[MyClassAttribute("DLPVideoApp.DLPCtrl.DLPWin")]的类。
Assembly exeAssembly = Assembly.GetExecutingAssembly(); Type[] types = exeAssembly.GetExportedTypes(); Type winType = null; for (int i = 0; i <= types.Length - 1; i++) { Type t = types[i]; if (!(t.IsPublic && t.IsClass == true && t.IsAbstract == false)) continue; object[] clsAttrs = t.GetCustomAttributes(typeof(MyClassAttribute), false); if (clsAttrs == null || clsAttrs.Length == 0) continue; MyClassAttribute myClassAtribute = clsAttrs[0] as MyClassAttribute; if (myClassAtribute.IdentifyType == "DLPVideoApp.DLPCtrl.DLPWin") { winType = t; break; } }
通过这种方式能够获取一个程序集中所有的类以及其它公开类型,通过遍历Type数组来寻找自定义的Attribute类,这种方式有时还是非常有效的,另外我们定义的MyClassAttribute的地方也添加了两个特性,分别是ComVisible和AttributeUsage,这两个用于定义当前特性的作用域,这个在使用的时候需要特别注意,在平常的开发过程中要记得去反思这样才能够真正地去熟练使用这些特性。