Unity:针对C#项目命名空间引用的检查过滤工具

前言

在工作中,发现有时会因为一些原因using了一些不适用于打包apk的命名空间导致打包失败,为项目的开发造成了不便,因此为项目做了一个小功能:在每次编译完成后自动进行命名空间的检查,根据设置的筛选条件检查是否存在不合理的命名空间引用
PS:最后会附上完整代码

整体思路

  • 首先需要两个类,一个为Util类:具体功能类,一个为Env类:存放一些设置好的过滤条件
  • 对目录进行设置好的过滤
  • 得到目录内的所有cs后缀的文件
  • 对文件进行设置好的过滤
  • 逐个遍历cs文件,以文本的方式获得前多少行的内容(或者可以自己设置一些条件如:读取到class声明就暂停之类的)
  • 对内容进行设置好的过滤
  • 检查每行的内容返回符合条件的文件信息统一存放
  • 完成检查,将所有信息打印
  • 最后可以用Stopwatch类的方法进行一层包装来计算整体步骤的耗时

正题内容

  • 首先是Env数据类:其中存放了几种过滤信息如下

    • 最顶级目录指定检查的目录(只检查这些,可以根据自己的项目自行规划
    • 过滤的目录名(这里是满足局部匹配就过滤
    • 过滤的宏定义(遇到这些宏定义时跳过
    • 需要检查的命名空间(这里的内容就是你要查找的符合条件的命名空间
  • 接下来是Util工具类,具体的功能实现都在这里面:

    • 首先我们先把过滤的方法实现:这里重载了一个方法用来获得目标含有的过滤字段
      			private static bool CheckHasFilter(List filterData,string fileName)
      		    {
      		        //......
      		    }
      		    
      		    private static bool CheckHasFilter(List filterData,string fileName,out List hasFilterName)
      		    {
      		        //......
      		    }
      		    
      
    • 实现对FileInfo类型文件进行命名空间检查的方法
      	/// 
          /// 对传入的文件信息FileInfo类型 进行内容检查
          /// 
          /// 
          private static List CheckFileInfo(FileInfo fileInfo)
          {
              //1.文件名过滤
              //2.获取内容
              //3.宏定义过滤
              //4.内容检查并存放数据
              //5.返回
              //......
          }
      
    • 接下来实现递归查找所有文件夹下的c#文件的方法并检查命名空间
      	/// 
          /// 主方法
          /// 
          /// 检查的根目录
          /// 
          public static Dictionary> CheckHasErrorNamespace(string checkFolderPath)
          {
              //1.得到目录下的C#文件,进行命名空间检查
              //2.得到目录下的子目录,递归调用该方法获得子目录的检查信息
              //3.返回得到的检查信息
              //......
           }
      
    • 最后,写个方法计算传入方法执行的时间损耗
      		/// 
      	    /// 执行并检查传入方法损耗的时间
      	    /// 
      	    /// 功能主体方法
      	    /// 自定义的功能名字
      	    private static void CheckUseTime(Action testFunc,string modelName)
      	    {
      	        Debug.Log($"开始:---{modelName}---");
      	        Stopwatch s = new Stopwatch();
      	        s.Reset();
      	        s.Start();
      	        testFunc?.Invoke();
      	        s.Stop();
      	        Debug.Log($"结束:本次---{modelName}---的运行时间 = {s.ElapsedMilliseconds} ms");
      	    }
      

最终检查效果如下:

每次编译时就会自动检测是否存在不合格的命名空间文件
Unity:针对C#项目命名空间引用的检查过滤工具_第1张图片

完整代码

  • Util类:
	using System;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.IO;
	using UnityEditor;
	using UnityEngine;
	using Debug = UnityEngine.Debug;
	
	public static class CheckNamespaceUtil
	{
	    
	    [MenuItem("Test/命名空间检查")]
	    [InitializeOnLoadMethod]
	    public static void CheckNamespace()
	    {
	        var checkNameList = CheckNamespaceEnv.checkName;
	        string msg = "命名空间检查内容:\n";
	        for (int i = 0; i < checkNameList.Count; i++)
	        {
	            msg += checkNameList[i] + ",";
	        }
	        Debug.Log(msg);
	        CheckUseTime(delegate
	        {
	            var checkInfos = CheckHasErrorNamespace(Application.dataPath);
	            foreach (var v in checkInfos)
	            {
	                for (int i = 0; i < v.Value.Count; i++)
	                {
	                    Debug.LogError($"名字:{v.Key}  含有命名空间字段:{v.Value[i]}  ------请及时清理");
	                }
	            }
	        }, "命名空间检查");
	    }
	    
	    /// 
	    /// 主方法
	    /// 
	    /// 检查的根目录
	    /// 
	    public static Dictionary> CheckHasErrorNamespace(string checkFolderPath)
	    {
	        //1.得到目录下的C#文件,进行命名空间检查
	        //2.得到目录下的子目录,递归调用该方法获得子目录的检查信息
	        //3.返回得到的检查信息
	        
	        Dictionary> checkInfo = new Dictionary>();;
	        
	        //1.
	        DirectoryInfo floder = new DirectoryInfo(checkFolderPath);
	        FileInfo[] fileInfos = floder.GetFiles("*.cs");
	        for (int i = 0; i < fileInfos.Length; i++)
	        {
	            if (checkInfo.ContainsKey(fileInfos[i].Name))
	            {
	                checkInfo[fileInfos[i].Name].AddRange(CheckFileInfo(fileInfos[i]));
	            }
	            else
	            {
	                checkInfo.Add(fileInfos[i].Name,CheckFileInfo(fileInfos[i]));
	            }
	        }
	        //2.
	        string[] dir = Directory.GetDirectories(checkFolderPath);
	        for (int i = 0; i < dir.Length; i++)
	        {
	            var data = CheckHasErrorNamespace(dir[i].Replace("\\", "/"));
	            foreach (var v in data)
	            {
	                if (checkInfo.ContainsKey(v.Key))
	                {
	                    checkInfo[v.Key].AddRange(v.Value);
	                }
	                else
	                {
	                    checkInfo.Add(v.Key,v.Value);
	                }
	            }
	        }
	        //PS:上面这样处理是为了 若有不同命名空间下的同名文件时,让他们合并
	        return checkInfo;
	    }
	
	    /// 
	    /// 对传入的文件信息FileInfo类型 进行内容检查
	    /// 
	    /// 
	    private static List CheckFileInfo(FileInfo fileInfo)
	    {
	        //1.文件名过滤
	        //2.获取内容
	        //3.宏定义过滤
	        //4.内容检查并存放数据
	        //5.返回
	        List checkInfo;
	        //1.
	        if (CheckHasFilter(CheckNamespaceEnv.filterFile, fileInfo.Name,out checkInfo))
	        {
	            return checkInfo;
	        }
	        //2.
	        StreamReader sr = fileInfo.OpenText();
	        string lineContent = "";
	        while ((lineContent = sr.ReadLine()) != null)
	        {
	        //3.
	            bool isMacroFilterOpen = false;//标志是否因为宏定义打开的跳过开关
	            //先判断是否处于宏定义内容中
	            bool isMacroDefineHead = CheckHasFilter(new List {"#if"}, lineContent);
	            if (isMacroDefineHead)
	            {
	                //过滤符合条件的宏定义
	                if (CheckHasFilter(CheckNamespaceEnv.filterMacroDefine, lineContent))
	                {
	                    isMacroFilterOpen = true;
	                    continue;
	                }
	            }
	            bool isMacroDefineFoot = CheckHasFilter(new List {"#endif"}, lineContent);
	            if (isMacroDefineFoot)
	            {
	                isMacroFilterOpen = false;
	                continue;
	            }
	            //根据开关跳过该行
	            if (isMacroFilterOpen)
	            {
	                continue;
	            }
	        //4.
	            List lineCheckInfo;
	            if (!CheckHasFilter(new List{"using"}, lineContent))
	            {
	                continue;
	            }
	            if (CheckHasFilter(CheckNamespaceEnv.checkName, lineContent, out lineCheckInfo))
	            {
	                checkInfo.AddRange(lineCheckInfo);
	            }
	        }
	
	        return checkInfo;
	    }
	    
	    #region 过滤方法
	    private static bool CheckHasFilter(List filterData,string fileName)
	    {
	        for (int i = 0; i < filterData.Count; i++)
	        {
	            int index = fileName.IndexOf(filterData[i]);
	            //返回结果等于-1表示匹配失败,匹配成功IndexOf会返回匹配的开始下标
	            if (index != -1)
	            {
	                return true;
	            }
	        }
	        return false;
	    }
	    
	    private static bool CheckHasFilter(List filterData,string fileName,out List hasFilterName)
	    {
	        hasFilterName = new List();
	        for (int i = 0; i < filterData.Count; i++)
	        {
	            int index = fileName.IndexOf(filterData[i]);
	            //返回结果等于-1表示匹配失败,匹配成功IndexOf会返回匹配的开始下标
	            if (index != -1)
	            {
	                hasFilterName.Add(filterData[i]);
	            }
	        }
	        if (hasFilterName.Count > 0)
	        {
	            return true;
	        }
	        return false;
	    }
	    #endregion
	    
	    /// 
	    /// 执行并检查传入方法损耗的时间
	    /// 
	    /// 功能主体方法
	    /// 自定义的功能名字
	    private static void CheckUseTime(Action testFunc,string modelName)
	    {
	        Debug.Log($"开始:---{modelName}---");
	        Stopwatch s = new Stopwatch();
	        s.Reset();
	        s.Start();
	        testFunc?.Invoke();
	        s.Stop();
	        Debug.Log($"结束:本次---{modelName}---的运行时间 = {s.ElapsedMilliseconds} ms");
	    }
	}
  • Env类:
	using System.Collections;
	using System.Collections.Generic;
	using UnityEngine;
	
	public static class CheckNamespaceEnv 
	{
	    public static int checkLineNum = 40;//检查前20行
	        
	    /// 
	    /// 检查的最上级文件夹列表
	    /// 
	    public static List checkFolder = new List
	    {
	        
	    };
	    /// 
	    /// 名字中含有这些词的文件过滤掉
	    /// 
	    public static List filterFile = new List
	    {
	        "TMP","Editor"
	    };
	    /// 
	    /// 名字中含有这些词的子文件夹过滤掉
	    /// 
	    public static List filterFolder = new List
	    {
	        "Editor","3rd","Extension","NavMeshComponents","Plugins","Generated"
	    };
	
	    /// 
	    /// 跳过这些宏定义
	    /// 
	    public static List filterMacroDefine = new List
	    {
	        "UNITY_EDITOR"
	    };
	        
	    /// 
	    /// 检查包含这些关键字的命名空间引用
	    /// 
	    public static List checkName = new List
	    {
	        "NPOI","UnityEditor"
	    };
	}

最后

其实很多地方都可以自己按照需求另外定制的,然后个人建议自己在内部匹配命名空间时加一个行数限制或者其他限制,避免每次都要检查整个文件,严重损耗性能。我这里呢在Env类中设置了行数这个参数,但写到这里才记起来忘记加了,但在我的这个例子中是无关紧要的。

有什么意见建议欢迎提出啦!-------来自一个刚刚开始工作的小萌新

你可能感兴趣的:(Unity辅助工具,Unity编辑模式工具)