定义键盘快捷键 qt lambda concurrency request camera vue数据绑定 后台ui模板 linux自动获取ip css选择器有几种 python操作mongodb python功能 java编程基础 java查看版本 java数据类型 java写入文件 java入门代码 java查看变量类型 linux教程 linux内核编程 嵌入式linux驱动程序设计从入门到精通 轮播图js代码 ps校正倾斜照片 联发科p70 刷机工具下载 电脑听歌识曲 windowsjs延时函数 mix2s拆机 hdcp是什么 打开组策略的命令 人马上单天赋 ai投影 cdr复制属性快捷键 笔记本摄像头软件 劲舞团辅助 rom定制大师 魔兽40m补丁 清泉流响哪里爆率高 方正正准黑简体 批量修改文件名软件
当前位置: 首页 > 学习教程  > 编程学习

c#类图生成器

2021/1/9 1:57:36 文章标签: ghost查看器

原文:http://www.codeproject.com/Articles/834517/Class-Diagram-Generator?msg4932808#xx4932808xx 作者是个老外,东西不是很难,基本都是反射,但是做出这样的工具,想法很好,便于开发人员快速了解dll文件…

原文:http://www.codeproject.com/Articles/834517/Class-Diagram-Generator?msg=4932808#xx4932808xx

作者是个老外,东西不是很难,基本都是反射,但是做出这样的工具,想法很好,便于开发人员快速了解dll文件内部结构,要给一个大大的赞!!!

下面是我测试的一个dll文件,且看生成的图片效果:


这仅仅是其中一张图,很明了吧。

原文内容摘录如下:

Introduction

This article targets class diagram generation and the in-ability of Visual Studio to export individual or all class diagrams as single files. We will look at Assemblies and how to get information like Fields, Properties, Methods and Events back from them. 

Download ClassDiagramGen_EXE.zip

Download source

Background

While all developers hate documentation and repetitive tasks it is still a part of our sometimes daily tasks.

Yes there are tools out there that ease the pain like Ghost Doc or Sandcastle Help Builder, but none of them actually generate class diagrams as well. The Code Project too houses a couple of class diagram generators but what makes this one different? Well... I'd say the ability to create a project with multiple entries pointing to dll's or exe that you wish to dump to class diagrams. This article will describe how I went about analyzing the dll or exe and how one then generates the class diagrams. Another feature of this code is to point it to the code directory and add remarks to the class headers; assisting with CHM generation that contains a visual representation of your class.

Using the code

Analyzing a .NET dll or exe.

[The following code is found in CDGenerator.cs]

        /// <summary>
        /// Analyzes the DLL.
        /// </summary>
        /// <param name="dllLocation">The DLL location.</param>
        /// <param name="excludeServices">if set to <c>true</c> [exclude services].</param>
        /// <param name="excludeCLR">if set to <c>true</c> [exclude color].</param>
        public static void AnalyzeDLL(string dllLocation, bool excludeServices = false, bool excludeCLR = true)
        {
            string assemblyNameSpace = "";
            Type currentType = null;
            Type[] typesFound = null;
            ClassList = new List<ClassInfo>();
            CDAssembly = null;

            try
            {
                if (dllLocation != string.Empty && File.Exists(dllLocation))
                {
                    CDAssembly = Assembly.LoadFrom(dllLocation);
                    if (CDAssembly.FullName != "")
                    {
                        assemblyNameSpace = CDAssembly.FullName.Substring(0, CDAssembly.FullName.IndexOf(","));

                        typesFound = CDAssembly.GetTypes();

                        if (typesFound != null)
                        {
                            foreach (var type in typesFound)
                            {
                                if (type.Namespace != null)
                                {
                                    if (type.IsNotPublic)
                                    {
                                        continue;
                                    }
                                    else
                                    {
                                        //Excludes Meta data classes and only generate for the current namespace
                                        if (!type.FullName.Contains("Metadata") && type.Namespace.Contains(assemblyNameSpace))
                                        {
                                            if (excludeServices && type.FullName.Contains("Service."))
                                            {
                                                continue;
                                            }

                                            //Exclude weird naming conventions.. usually generated classes not coded by a developer
                                            if (!type.FullName.Contains("<") && !type.FullName.Contains(">"))
                                            {
                                                currentType = type;
                                                ClassList.Add(new ClassInfo(Path.GetFileName(dllLocation), type, excludeCLR));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("{0}: {1}", currentType.Name, ex.Message));
                throw ex;
            }
        }

Have a look at the AnalyzeDLL method above. You will see a few helper classes used inside.
ClassInfo : Stores information like Class Type, Name and a List of BasicInfo found inside the dll or exe.
BasicInfo :  Stores the info type (e.g. Fields, Properties, Constructor, Methods or Events).

The code starts by setting a couple of variable and doing a couple of checks to ensure we actually passed it a valid location to the dll or exe to analyze.

The actual work of loading the dll or exe into memory is done by the following command.

CDAssembly = Assembly.LoadFrom(dllLocation);

I use Assembly.LoadFrom() to ensure that we don't get any errors with referenced assemblies that possibly lay in the same directory as the loaded file. This is generally the problem when loading a dll or exe with Assembly.Load().

To ensure that the file is actually loaded a check is done on the assemblies full name after wich the namespace is recorded into the assemblyNameSpace variable.

A array of types are extracted from the assembly by using the GetTypes() method on our CDAssembly.

Further checks are done to see that types are actually found and that they are publically accessible.
Meta data is excluded and a check is done that we only look at methods in the main Namespace.

If all these checks pass the current type is added to the ClassList where a new ClassInfo item is created.

ClassList.Add(new ClassInfo(Path.GetFileName(dllLocation), currentType, excludeCLR));

By looking closer at the ClassInfo constructor you will find that a check is done on the type of class (e.g. Class, Interface, AbstractClass, Enum or Struct).

Once we know what type the class is the GetMoreInfo() method is called. This method finds all the Properties, Fields, Constructors, Methods and Events.

All of these are found in the same fashion.

Let's have a look at how the Fields are found:

FieldInfo[] fieldInfos = referenceType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.CreateInstance);

An array is built up by checking the assembly type and calling the GetMethods() method. This holds true for Fields(GetFields()), Properties (GetProperties()) etc. Flags are set to Public , Static , Instance or CreateInstance.

if (methodInfos != null && methodInfos.Length > 0)
                {
                    foreach (MethodInfo methodInfo in methodInfos)
                    {
                        //Only add custom methods. Don't show built in DotNet methods
                        if (excludeCLR && methodInfo.Module.ScopeName != ReferenceFileName)
                        {
                            continue;
                        }
                        else
                        {
                            ClassInformation.Add(new BasicInfo(BasicInfo.BasicInfoType.Methods, methodInfo.Name, methodInfo.ReturnType));
                        }
                    }
                }

Once the array has been built up a check is done to check for nulls and that the array actually contains records.
When the check passes we loop through all the methods found whilst checking if the method falls within the scope of the file passed and if built in .NET fields must be excluded. An example of a built in .NET method would be ToString() or GetType().

Generating a Class Diagram


public static void GeneratesClassDiagram(ClassInfo classInfo, string outputPath, bool includeProperties = true, bool includeMethods = true, bool includeEvents = true, bool includeFields = true, bool includeConstructor = true)
        {... }
​

 Now that we have a Class (ClassInfo) with lists of all its Constructor, Fields, Properties,  Methods and Events we can actually generate an class diagram image.
 This is all done by the GeneratesClassDiagram() method contained in the CDGenerator   Class.

 A helper method and class is used inside GeneratesClassDiagram. 
 CalculateSize : Calculates the image size for the new class diagram based on how many  constructors, fields, properties, methods and events are present.

 RoundedRectangle :  This class creates a graphic path for GDI+ to draw. It aids with calculating  the rounded corners based on the given width, height and diameter. 

Graphics are drawn in 'Layers' to represent the class box and its shadow along with a gradient title containing the class name and type. Based on configuration settings each class type can have a different color.

AntiAliasing is applied to enhance text readability.

//Enable AntiAliasing
g.TextRenderingHint = TextRenderingHint.AntiAlias;

Finally when all the 'Layers' are drawn; a size comparison is done to avoid saving empty class diagrams.

Remarking the Code

Remarking the code is done by calling the RemarkAllImages() method.
It takes the code path and image path as variables and then proceeds to "Remark" all classes in code that have a resulting class diagram image.

/// <summary>
        /// Remarks all images.
        /// </summary>
        /// <param name="codePath">The code path.</param>
        /// <param name="imagesPath">The images path.</param>
        public static void RemarkAllImages(string codePath, string imagesPath)
        {
            string currentClassName = "";
            string remarkTemplate = Properties.Settings.Default.RemarkTemplate;
            string searchString = "";
            string currentClassRemark = "";

            int startIndex = 0;
            StreamReader sr;
            StreamWriter sw;
            FileInfo currentCodeFile;
            string currentCodeFileText = "";
            string outCodeFileText = "";
            if (Directory.Exists(imagesPath))
            {
                DirectoryInfo codeDirInfo = new DirectoryInfo(codePath);

                FileUtils.GetAllFilesInDir(codeDirInfo, "*.cs");

                foreach (string fileName in Directory.GetFiles(imagesPath, "*.png"))
                {
                    startIndex = 0;
                    try
                    {
                        currentClassName = Path.GetFileName(fileName).Replace(".png", "");
                        currentCodeFile = FileUtils.files.Where(f => f.Name == string.Format("{0}.cs", currentClassName)).FirstOrDefault();

                        if (currentCodeFile != null)
                        {
                            using (sr = new StreamReader(currentCodeFile.FullName))
                            {
                                currentCodeFileText = sr.ReadToEnd();
                                sr.Close();

                                //Finding the class description logic goes here. In essence it results in something like this:
                                //searchString = string.Format("public {0}", currentClassName);
                                

                                startIndex = currentCodeFileText.IndexOf(searchString);

                                //Add the remark
                                currentClassRemark = string.Format("{0}{1}\t", string.Format(remarkTemplate, currentClassName), Environment.NewLine);

                                if (!currentCodeFileText.Contains(currentClassRemark))
                                {
                                    outCodeFileText = currentCodeFileText.Insert(startIndex, currentClassRemark);

                                    using (sw = new StreamWriter(currentCodeFile.FullName, false))
                                    {
                                        sw.WriteLine(outCodeFileText);
                                        sw.Close();
                                    }
                                }

                                if (RemarkDone != null)
                                {
                                    RemarkDone(currentClassName);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        ErrorOccurred(ex, null);
                    }
                }
            }
        }

Have a look at the RemarkAllImages method above. 

It makes use of the FileUtils class to find all C# code files:

FileUtils.GetAllFilesInDir(codeDirInfo, "*.cs");

The method has a loop that then gets all png files and with the help of LINQ it filters out all code files that do not have a resulting image.

FileUtils.files.Where(f => f.Name == string.Format("{0}.cs", currentClassName)).FirstOrDefault();

A bit of logic is applied to build up a search string based on the class type.
The search string then finds the class header and adds the remark above that using the remark template.

Once the string is appended it is written back to the code file.

Points of Interest

We already use this tool quite extensively at our place of work and it is in the process of being expanded to include EF helper class generation, SQL Database commenting from comments in code and database dictionary generation. Hopefully one-day I can upload it here as a tool that can assist an even greater audience.



本文链接: http://www.dtmao.cc/news_show_1100140.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?