還在手畫C#依賴關系圖嗎?快來試試這個工具吧!

還在手畫C#依賴關系圖嗎?快來試試這個工具吧!

筆者最近見到了一個不錯的工具,可以讓大家在看代碼的時候一鍵生成C#依賴的類圖。非常適合編寫文檔、查看和學習開源項目設計時使用,比如下方就是筆者通過這個工具生成的Microsoft.Extensions.ObjectPool依賴圖,可以非常清晰明了的告訴我們類與類之間的關系。

GITHUB地址:

https://github.com/pierre3/PlantUmlClassDiagramGenerator

4a1e4aef0f1aec0c1867f6962386bf6f.png
image-20221107232048503

介紹PlantUmlClassDiagramGenerator

這是一個生成器,用于從C#源代碼中創建PlantUML的類圖。

Visual Studio Code 擴展

  • C# to PlantUML[1]

aaa32791c4d573592d6e938ecf3e206c.png
image-20221107232754756

.Net Core 全局工具

Nuget Gallery: https://www.nuget.org/packages/PlantUmlClassDiagramGenerator

安裝

下載并安裝.NET 6.0 SDK[2]或更新的版本。安裝后,運行以下命令。

dotnet tool install --global PlantUmlClassDiagramGenerator

使用

運行 "puml-gen" 命令.

puml-gen InputPath [OutputPath] [-dir] [-public | -ignore IgnoreAccessibilities] [-excludePaths ExcludePathList] [-createAssociation]
  • InputPath: (必須) 設置一個輸入源文件或目錄名稱。

  • OutputPath: (可選) 設置一個輸出文件或目錄名稱。如果省略此選項,plantuml文件將被輸出到與輸入文件相同的目錄中。

  • -dir: (可選) 當InputPath和OutputPath為目錄名時,指定。

  • -public: (可選) ?如果指定,只輸出公共可及性成員。

  • -ignore: (可選) 指定要忽略的成員的可訪問性,用逗號分隔的列表。

  • -excludePaths: (可選) 指定排除的文件和目錄。
    指定來自 "InputPath "的相對路徑,用逗號分隔的列表。

  • -createAssociation: (可選) 從字段和屬性的引用中創建對象關聯。

  • -allInOne: (可選) 只有當-dir被設置時:將所有圖表的輸出復制到文件include.puml(這允許PlanUMLServer渲染)。

  • -attributeRequired: (可選) 當這個開關被啟用時,只有類型聲明中帶有 "PlantUmlDiagramAttribute "的類型會被輸出。

例子:

puml-gen C:\Source\App1\ClassA.cs -public
puml-gen C:\Source\App1 C:\PlantUml\App1 -dir -ignore Private,Protected -createAssociation -allInOne
puml-gen C:\Source\App1 C:\PlantUml\App1 -dir -excludePaths bin,obj,Properties

生成好以后,會在對應的目錄看到有一些*.puml的文件,然后可以用對應的PlantUML工具打開。

筆者這里使用的是Visual Studio Code打開的PlantUML圖,需要安裝一個插件,可能某些電腦需要安裝Java環境。

8bfd2d3b8591518c11b93edb5e237911.png
image-20221107232315656

打開以后按Alt+D,或者右擊 -> 預覽光標位置圖表就可以了,當然右擊也可以導出圖片。

1b63cc897e01fe2347c8b0e6cee5aa82.png
image-20221107232533751

下文是puml-gen工具對PlantUML的一些轉換規則,大家有興趣的可以了解一下。

轉換為PlantUML的規范

類型聲明

Type 關鍵字

C#PlantUML
classclass
struct<<struct>> class
interfaceinterface
enumenum
record<<record>> class

Type 修飾符

C#PlantUML
abstractabstract
static<<static>>
partial<<partial>>
sealed<<sealed>>
  • C#

class?ClassA?{??
}
struct?StructA?{
}
interface?InterfaceA?{
}
record?RecordA?{
}
abstract?class?AbstractClass?{
}
static?class?StaticClass?{
}
sealed?partial?class?ClassB{
}
enum?EnumType{Apple,Orange,Grape
}
  • PlantUML

@startuml
class ClassA {
}
class StructA <<struct>> {
}
interface InterfaceA {
}
class RecordA <<record>> {
}
abstract class AbstractClass {
}
class StaticClass <<static>> {
}
class ClassB <<sealed>> <<partial>> {
}
enum EnumType {Apple,Orange,Grape,
}
@enduml
e2802776fa57242512fcb3e69346da53.png
TypeDeclaration.png

泛型

  • C#

class?GenericsType<T1>{
}
class?GenericsType<T1,T2>{
}
  • PlantUML

class "GenericsType`1"<T1>{
}
class "GenericsType`2"<T1,T2>{
}
6bc0fefe426731349ec4f4bdcc008545.png
GenericsTypeDeclaration.png

成員聲明

可見性修飾符

C#PlantUML
public+
internal<<internal>>
protected internal# <<internal>>
protected#
private-

修飾符

C#PlantUML
abstract{abstract}
static{static}
virtual<<virtual>>
override<<override>>
new<<new>>
readonly<<readonly>>
event<<event>>

屬性訪問器

C#PlantUML
int Prop {get; set;}Prop : int <<get>> <<set>>
int Prop {get;}Prop : int <get>
int Prop {get; private set }Prop : int <<get>><<private set>>
int Prop => 100;Prop : int <<get>>
  • C#

abstract?class?AbstractClass
{protected?int?_x;internal?int?_y;protected?internal?int?_z;public?abstract?void?AbstractMethod();protected?virtual?void?VirtualMethod(string?s){}public?string?BaseMethod(int?n){return?"";}
}
class?ClassM?:?AbstractClass
{public?static?readonly?double?PI?=3.141592;public?int?PropA?{?get;?set;?}public?int?PropB?{?get;?protected?set;?}public?event?EventHandler?SomeEvent;public?override?void?AbstractMethod(){}protected?override?void?VirtualMethod(string?s){}public?override?string?ToString(){return?"override";}public?new?string?BaseMethod(int?n){return?"new";}
}
  • PlantUML

abstract class AbstractClass {# _x : int<<internal>> _y : int# <<internal>> _z : int+ {abstract} AbstractMethod() : void# <<virtual>> VirtualMethod(s:string) : void+ BaseMethod(n:int) : string
}
class ClassM {+ {static} <<readonly>> PI : double = 3.141592+ PropA : int <<get>> <<set>>+ PropB : int <<get>> <<protected set>>+  <<event>> SomeEvent : EventHandler + <<override>> AbstractMethod() : void# <<override>> VirtualMethod(s:string) : void+ <<override>> ToString() : string+ <<new>> BaseMethod(n:int) : string
}
AbstractClass <|-- ClassM
14edb26d3e34072fe482fe4f4b4d67f1.png
MemberDeclaration.png

字段和屬性初始化

只有常量的初始化才會被輸出。

  • C#

class?ClassC
{private?int?fieldA?=?123;public?double?Pi?{get;}?=?3.14159;protected?List<string>?Items?=?new?List<string>();?
}
  • PlantUML

class ClassC {- fieldA : int = 123+ Pi : double = 3.14159# Items : List<string>
}
0db9704b49fb90ae862f7248bed6bde2.png
Initializer.png

嵌套類聲明

嵌套類被展開并與 "OuterClass + - InnerClass "關聯。

  • C#

class?OuterClass?
{class?InnerClass?{struct?InnerStruct?{}}
}
  • PlantUML

class OuterClass{}
class InnerClass{}
<<struct>> class InnerStruct {}
OuterClass +- InnerClass
InnerClass +- InnerStruct
190785d8a81c9b408ab03df603c34692.png
NestedClass.png

繼承關系

  • C#

abstract?class?BaseClass
{public?abstract?void?AbstractMethod();protected?virtual?int?VirtualMethod(string?s)?=>?0;
}
class?SubClass?:?BaseClass
{public?override?void?AbstractMethod()?{?}protected?override?int?VirtualMethod(string?s)?=>?1;
}interface?IInterfaceA?{}
interface?IInterfaceA<T>:IInterfaceA
{T?Value?{?get;?}
}
class?ImplementClass?:?IInterfaceA<int>
{public?int?Value?{?get;?}
}
  • PlantUML

abstract class BaseClass {+ {abstract} AbstractMethod() : void# <<virtual>> VirtualMethod(s:string) : int
}
class SubClass {+ <<override>> AbstractMethod() : void# <<override>> VirtualMethod(s:string) : int
}
interface IInterfaceA {
}
interface "IInterfaceA`1"<T> {Value : T <<get>>
}
class ImplementClass {+ Value : int <<get>>
}
BaseClass <|-- SubClass
IInterfaceA <|-- "IInterfaceA`1"
"IInterfaceA`1" "<int>" <|-- ImplementClass
825b269e0801e7b50b3e20ed3464fb64.png
InheritanceRelationsips.png

關聯(來自字段和屬性的引用)

如果你指定了 "createAssociation "選項,對象關聯將從字段和屬性引用中創建。

  • C#

class?ClassA{public?IList<string>?Strings{get;}?=?new?List<string>();public?Type1?Prop1{get;set;}public?Type2?field1;
}class?Type1?{public?int?value1{get;set;}
}class?Type2{public?string?string1{get;set;}public?ExternalType?Prop2?{get;set;}
}
  • PlantUML

@startuml
class ClassA {
}
class Type1 {+ value1 : int <<get>> <<set>>
}
class Type2 {+ string1 : string <<get>> <<set>>
}
class "IList`1"<T> {
}
ClassA o-> "Strings<string>" "IList`1"
ClassA --> "Prop1" Type1
ClassA --> "field1" Type2
Type2 --> "Prop2" ExternalType
@enduml
0934fbff2ce0aad53dfeb02aa590d728.png
InheritanceRelationsips.png

記錄類型(含參數列表)

C# 9中的記錄類型可以有一個參數列表。在這些情況下,這些參數 被作為屬性添加到類中。

  • C#

record?Person(string?Name,?int?Age);record?Group(string?GroupName)?{public?Person[]?Members?{?get;?init;?}
}
  • PlantUML

@startuml
class Person <<record>> {+ Name : string <<get>> <<init>>+ Age : int <<get>> <<init>>
}
class Group <<record>> {+ GroupName : string <<get>> <<init>>+ Members : Person[] <<get>> <<init>>
}
@enduml
58894db4c2b18d5c311cc0ea88796b7f.png
InheritanceRelationsips.png

基于特性的配置

你可以將PlantUmlClassDiagramGenerator.Attributes[3]包添加到你的C#項目中,用于基于特性的配置。

PlantUmlDiagramAttribute

只有被添加了PlantUmlDiagramAttribute的類型才會被輸出。如果-attributeRequired開關被添加到命令行參數中,這個屬性就會被啟用。

這個屬性只能被添加到類型聲明中。

  • class

  • struct

  • enum

  • record

class?ClassA
{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}
}[PlantUmlDiagram]
class?ClassB
{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}
}

只有帶有PlantUmlDiagramAttribute的ClassB會被輸出。

@startuml
class ClassB {+ Name : string <<get>> <<set>>+ Age : int <<get>> <<set>>
}
@enduml

PlantUmlIgnoreAttribute

添加了這個屬性的元素被排除在輸出之外。

[PlantUmlIgnore]
class?ClassA
{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}
}class?ClassB
{public?string?Name?{?get;?set;?}[PlantUmlIgnore]public?int?Age?{?get;?set;?}
}class?ClassC
{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}[PlantUmlIgnore]public?ClassC(string?name,?int?age)?=>?(Name,?Age)?=?(name,?age);public?void?MethodA();[PlantUmlIgnore]public?void?MethodB();
}
@startuml
class ClassB {+ Name : string
}
class ClassC {+ Name : string+ Age : int+ MethodA() : void
}
@enduml

PlantUmlAssociationAttribute

通過添加這個屬性,你可以定義類之間的關聯。這個屬性可以被添加到屬性、字段和方法參數。

關聯的細節被定義在以下屬性中。

  • Name

    • 指定葉子節點一側的類型名稱。

    • 如果省略,則使用添加該屬性的元素的名稱。

  • Association

    • 指定關聯的邊緣部分。在PlantUML中設置一個有效的字符串。

    • 如果省略,則使用"--"。

  • RootLabel

    • 指定顯示在根節點一側的標簽。

    • 如果省略,則不顯示。

  • Label

    • 指定要顯示在邊緣中心的標簽。

    • 如果省略,則不顯示。

  • LeafLabel

    • 指定顯示在葉子節點一側的標簽。

    • 如果省略,則不顯示。

class?Parameters
{public?string?A?{?get;?set;?}public?string?B?{?get;?set;?}
}class?CustomAssociationSample
{[PlantUmlAssociation(Name?=?"Name",?Association?=?"*-->",?LeafLabel?=?"LeafLabel",?Label=?"Label",?RootLabel?=?"RootLabel")]?public?ClassA?A?{?get;?set;?}
}class?CollectionItemsSample
{[PlantUmlAssociation(Name?=?"Item",?Association?=?"o--",?LeafLabel?=?"0..*",?Label?=?"Items")]public?IList<Item>?Items?{?get;?set;?}
}class?MethodParamtersSample
{public?void?Run([PlantUmlAssociation(Association?=?"..>",?Label?=?"use")]?Parameters?p){Console.WriteLine($"{p.A},{p.B}");}private?ILogger?logger;public?MyClass([PlantUmlAssociation(Association?=?"..>",?Label?=?"Injection")]?ILogger?logger){this.logger?=?logger;}
}
@startuml
class Parameters {+ A : string <<get>> <<set>>+ B : string <<get>> <<set>>
}
class CustomAssociationSample {
}
class CollectionItemsSample {
}
class MethodParamtersSample {+ Run(p:Parameters) : void+ MyClass(logger:ILogger)
}
CustomAssociationSample "RootLabel" *--> "LeafLabel" Name : "Label"
CollectionItemsSample o-- "0..*" Item : "Items"
MethodParamtersSample ..> Parameters : "use"
MethodParamtersSample ..> ILogger : "Injection"
@enduml
2690228fb64179c97c9ef0ed65e09797.png
CustomAssociation.png

PlantUmlIgnoreAssociationAttribute

這個屬性可以被添加到屬性和字段中。具有此屬性的屬性(或字段)被描述為類的成員,沒有任何關聯。

class?User
{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}
}class?ClassA
{public?static?User?DefaultUser?{?get;?}public?IList<User>?Users?{?get;?}public?ClassA(IList<User>?users){Users?=?users;DefaultUser?=?new?User(){Name?=?"DefaultUser",Age?=?"20"};}
}class?ClassB
{[PlantUmlIgnoreAssociation]public?static?User?DefaultUser?{?get;?}[PlantUmlIgnoreAssociation]public?IList<User>?Users?{?get;?}public?ClassB(IList<User>?users){Users?=?users;DefaultUser?=?new?User(){Name?=?"DefaultUser",Age?=?"20"};}
}
@startuml
class User {+ Name : string <<get>> <<set>>+ Age : int <<get>> <<set>>
}
class ClassA {+ ClassA(users:IList<User>)
}
class ClassB {+ {static} DefaultUser : User <<get>>+ Users : IList<User> <<get>>+ ClassB(users:IList<User>)
}
class "IList`1"<T> {
}
ClassA --> "DefaultUser" User
ClassA --> "Users<User>" "IList`1"
@enduml
36b96e2920954fdbcd465cc7eb31601f.png
IgnoreAssociation.png

參考資料

[1]

C# to PlantUML: https://marketplace.visualstudio.com/items?itemName=pierre3.csharp-to-plantuml

[2]

.NET 6.0 SDK: https://www.microsoft.com/net/download/windows

[3]

PlantUmlClassDiagramGenerator.Attributes: https://www.nuget.org/packages/PlantUmlClassDiagramGenerator.Attributes

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281255.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281255.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281255.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Web服務器 - Apache配置介紹

基本語法 常量的定義與使用&#xff0c;使用關鍵詞 Define 可以定義常量&#xff0c;使用 ${} 插入常量&#xff0c;如下 語法規則說明示列Define定義常量Define SRVROOT “D:/srv/Apache24”${}使用常量ServerRoot “${SRVROOT}”/表示路徑時使用 / 而不使用 \D:/srv/Apache…

點火開關分為4個檔位,分別是off,acc,IG-on,和ST

off全車除了常火&#xff08;如應急燈&#xff0c;時鐘等的記憶功能&#xff09;外&#xff0c;均不供電。acc 是附件檔&#xff0c;部分車載附屬設備供電&#xff0c;如視聽系統&#xff0c;儀表燈&#xff0c;燈光等。也就是說&#xff0c;車停在哪里&#xff0c;發動機不轉&…

h5的formData 上傳文件及.net后臺

先來前端的代碼&#xff1a; html 代碼&#xff1a; <input type"file" id"files" value"" multiple/> js代碼&#xff1a; function init() {var ele_files document.querySelector("#files");ele_files.addEventListener(&qu…

51 Nod 1027 大數乘法【Java大數亂搞】

1027 大數乘法 基準時間限制&#xff1a;1 秒 空間限制&#xff1a;131072 KB 分值: 0 難度&#xff1a;基礎題 給出2個大整數A,B&#xff0c;計算A*B的結果。Input第1行&#xff1a;大數A 第2行&#xff1a;大數B (A,B的長度 < 1000&#xff0c;A,B > 0&#xff09; Out…

關于ASP.NET Core WebSocket實現集群的思考

前言提到WebSocket相信大家都聽說過&#xff0c;它的初衷是為了解決客戶端瀏覽器與服務端進行雙向通信&#xff0c;是在單個TCP連接上進行全雙工通訊的協議。在沒有WebSocket之前只能通過瀏覽器到服務端的請求應答模式比如輪詢&#xff0c;來實現服務端的變更響應到客戶端&…

windows環境下Apache+PHP+MySQL搭建服務器

相關文件下載 下載地址Apachehttps://www.apachehaus.com/cgi-bin/download.plxPHPhttps://windows.php.net/downloadMySQLhttps://dev.mysql.com/downloads/mysql/MySQL MySQL配置 當前使用的MySQL版本是8.0.18&#xff0c;在MySQL根目錄下新建my.ini文件&#xff0c;下面是…

angular.js國際化模塊

最近需要將一個項目轉化成英文的&#xff0c; 于是發現一個angular模塊angular-translate&#xff0c;實現如下&#xff1a; 1.安裝包 bower install angular-translate bower install angular-translate-loader-static-files //然后在頁面引用進去 <script src"/angul…

觸屏網站如何實現返回并刷新

目的 在會員中心等頁面常常會遇到進入內頁修改信息&#xff0c;返回前一個頁面需要更新信息的場景。 思路 用COOKIE記錄當前頁面是否需要刷新&#xff0c;返回之后再刷新一次頁面。 方案 下載js.cookie.js然后引入到項目中 https://github.com/js-cookie/js-cookie 先來一個最簡…

更快,更強的.NET 7 發布了

.NET Conf 2022 在昨晚(11?8?) 11 點 正式開始了&#xff0c;為期三天的會議&#xff08;11?8-10?&#xff09;&#xff0c; 圍繞 .NET 7 展開。相信各位?伙伴都已經開始安裝 .NET 7 正式版本還有以及相關的開發?具。這次 .NET 7 圍繞傳統的 C# &#xff0c;ASP.NET Core…

Web服務器 - Nginx配置介紹

nginx的配置相對簡單&#xff0c;總體來說分為5種模塊 全局塊&#xff1a;配置影響nginx全局的指令。一般有運行nginx服務器的用戶組&#xff0c;nginx進程pid存放路徑&#xff0c;日志存放路徑&#xff0c;配置文件引入&#xff0c;允許生成worker process數等。events塊&…

jvm(Java virtual machine) JVM架構解釋

2019獨角獸企業重金招聘Python工程師標準>>> JVM 架構解釋 每個Java開發者都知道通過JRE【Java運行環境】執行字節碼。 但是很多人都不知道JRE是JVM實現的事實。JVM負責執行字節碼的分析 代碼的解釋和運行。 我們應該了解JVM的架構&#xff0c;這對開發者來說是很重…

Hyper-V 嵌套虛擬化

先決條件運行 Windows Server 2016 或Windows 10 周年更新的 Hyper-V 主機。運行 Windows Server 2016 或Windows 10 周年更新的 Hyper-V VM。配置版本為 8.0 或更高的 Hyper-V VM。采用 VT-x 和 EPT 技術的 Intel 處理器&#xff08;AMD-V技術的暫時不支持&#xff09;>Set…

簡單的面試題簡解思路(搜集)

1. 統計字符串中單詞出現次數 "hi how are you i am fine thank you youtube am am "&#xff0c;統計"you"出現的次數。 方法一 : split() function wordCount(str,word){var str str || "";var word word || "";var strArr s…

WinForm(十五)窗體間通信

在很多WinForm的程序中&#xff0c;會有客戶端之間相互通信的需求&#xff0c;或服務端與客戶端通信的需求&#xff0c;這時就要用到TCP/IP的功能。在.NET中&#xff0c;主要是通過Socket來完成的&#xff0c;下面的例子是通過一個TcpListerner作為監聽&#xff0c;等待TcpClie…

905. 按奇偶排序數組

1// 905. 按奇偶排序數組 2/** 3 * param {number[]} A 4 * return {number[]} 5 */ 6var sortArrayByParity function(A) { 7 return A.filter(value > value % 2 0).concat( 8 A.filter(value > value % 2 1) 9 )10}; 轉載于:https://www.cnblogs.com/…

關于Java開發需要注意的十二點流程

1.將一些需要變動的配置寫在屬性文件中 比如&#xff0c;沒有把一些需要并發執行時使用的線程數設置成可在屬性文件中配置。那么你的程序無論在DEV環境中&#xff0c;還是TEST環境中&#xff0c;都可以順暢無阻地運行&#xff0c;但是一旦部署在PROD上&#xff0c;把它作為多線…

Unity經典游戲教程之:雪人兄弟

版權聲明&#xff1a; 本文原創發布于博客園"優夢創客"的博客空間&#xff08;網址&#xff1a;http://www.cnblogs.com/raymondking123/&#xff09;以及微信公眾號"優夢創客"&#xff08;微信號&#xff1a;unitymaker&#xff09;您可以自由轉載&#x…

使用webpack搭建個性化項目

安裝主包 yarn add webpack webpack-cli webpack-dev-server -D根據項目實際需求安裝loaders&#xff0c;webpack-loaders列表 根據項目實際需求安裝插件&#xff0c; webpack-plugins列表 常用包列表 包名說明webpackwebpack主程序&#xff0c;配置列表webpack-cliwebpack…

.NET周報【11月第1期 2022-11-07】

國內文章開源安全賦能 - .NET Conf China 2022https://mp.weixin.qq.com/s/_tYpfPeQgyEGsnR4vVLzHg.NET Conf China 2022 是面向開發人員的社區峰會&#xff0c;延續 .NET Conf 2022 的活動&#xff0c;慶祝 .NET 7 的發布和回顧過去一年來 .NET 在中國的發展成果&#xff0c;它…

React - 狀態提升

從入門的角度來聊一下React 的狀態提升。我們先來看一下React官網是怎么介紹這一概念的&#xff1a;使用 react 經常會遇到幾個組件需要共用狀態數據的情況。這種情況下&#xff0c;我們最好將這部分共享的狀態提升至他們最近的父組件當中進行管理。很簡單的一句介紹&#xff0…