C#的AOP(最經典實現)

来源:https://www.cnblogs.com/dotnet-college/p/18128903
-Advertisement-
Play Games

(適用於.NET/.NET Core/.NET Framework) 【目錄】0.前言1.第一個AOP程式2.Aspect橫切麵編程3.一個橫切麵程式攔截多個主程式4.多個橫切麵程式攔截一個主程式5.AOP的泛型處理(擴充)6.AOP的非同步處理(擴充)7.優勢總結8.展望 0.前言 AOP(Aspe ...


(適用於.NET/.NET Core/.NET Framework)

【目錄】
0.前言
1.第一個AOP程式
2.Aspect橫切麵編程
3.一個橫切麵程式攔截多個主程式
4.多個橫切麵程式攔截一個主程式
5.AOP的泛型處理(擴充)
6.AOP的非同步處理(擴充)
7.優勢總結
8.展望

0.前言

AOP(Aspect Oriented Programming)是“面向橫切麵編程”,主要是用來對程式/模塊進行解耦。怎麼理解??

我們可以把一般的編程理解為“縱向編程”(主程式),比如如下的一個示例代碼:

        public string GetInfo(int i)
        {
            string s = "";

            if (i == 1)
                s = "A";
            else if (i == 2)
                s = "B";
            else if (i == 3)
                s = "C";
            else
                s = "Z";

            return s;
        }

試想一下,上述軟體實際使用後,

  • 如果條件變數i有更多的判斷值,我們是不是要在GetInfo()方法內部修改代碼+重新編譯?
  • 如果後續需要加個日誌記錄功能,我們是不是也要在GetInfo()方法內部加上日誌函數+重新編譯?
  • 如果...
  • 更多如果...

為了避免上述的這些麻煩並增加軟體的靈活性,“橫向編程”,也就是AOP被創造了出來,它就像是“橫切一刀”,把相關功能塞進了主程式。

 

現行AOP的實現,主要是通過攔截方法(即攔截主程式),並修改其參數+返回值來完成。

網上有很多相關方案,比如:特性註釋攔截、動態代碼生成、派遣代理模式、等。但這些方案要麼實現的很複雜、要麼耦合度沒完全切斷、邏輯有變化時還是需要修改代碼重新編譯。均不夠理想。

而今天要隆重登場的主角-DeveloperSharp平臺中的AOP技術,則提供了一種簡便、快捷、徹底解耦的AOP實現。使用它,在程式邏輯有變化時,你只需要修改配置文件就行,而不再需要對主程式進行一丁丁點的代碼修改!!

1.第一個AOP程式

製作一個AOP程式需要四個步驟:

(1)製作主程式

(2)製作橫切麵程式

(3)製作配置文件,讓橫切麵程式攔截主程式

(4)調用主程式

下麵,我們一步一步來實現上述四個步驟。

【第一步】:製作主程式

我們在Visual Studio中新建一個名為“School.Logic”的類庫工程,併在該工程中新建一個名為PersonManage的類,該類中有一個名為GetInfo1的方法,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class PersonManage : LogicLayer
    {
        public string GetInfo1(string Name, int Num)
        {
            return $"共有{Name}{Num}人";
        }
    }
}

以上,編寫了一個非常簡單的主程式。

 

【第二步】:製作橫切麵程式

我們再在Visual Studio中新建一個名為“School.Aspect”的類庫工程,併在該工程中新建一個名為Interceptor1的類,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的兩個參數值改掉
            e.MethodInfo.ParameterValues[0] = "老師";
            e.MethodInfo.ParameterValues[1] = 20;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {
            
        }
    }
}

 以上,編寫了一個橫切麵程式。它的主要功能是把主程式方法的兩個參數值給改掉。
AspectModel基類中的PreProcess方法會在主程式方法執行之前被執行,而PostProcess方法會在主程式方法執行之後被執行。它兩就是AOP橫向攔截的核心要素。它兩均需要被override重寫覆蓋掉。

 

【第三步】:製作配置文件,讓橫切麵程式攔截主程式

若是在.Net Core環境下,我們創建一個名為DeveloperSharp.json的配置文件,設置讓Interceptor1攔截PersonManage中的GetInfo1方法。文件內容如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1",  //橫切麵攔截器類
           "scope":"School.Logic.PersonManage",  //被攔截的主程式類
           "method":"GetInfo1"                   //被攔截的方法
         }
     ]
  }
}

若是在.Net Framework環境下,我們創建一個名為DeveloperSharp.xml的配置文件,設置讓Interceptor1攔截PersonManage中的GetInfo1方法。文件內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

註意:以上配置中所有的類名,都要用完全限定名。

 

【第四步】:調用主程式

最後,我們再在Visual Studio中創建一個控制台工程,讓它來調用主程式中的GetInfo1方法,代碼如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
            var pm = new School.Logic.PersonManage();

            //要用這種形式調用主程式中的方法,AOP功能才會生效
            var str = pm.InvokeMethod("GetInfo1", "學生", 200);
            Console.WriteLine(str);

            Console.ReadLine();
        }

附註:有人會覺得上述InvokeMethod這種調用方法不夠優雅,但事實上ASP.NET Web Api也是被類似InvokeMethod這種方式包裹調用才實現了各種Filter攔截器的攔截(本質也是AOP),只不過它的這個InvokeMethod動作是在.NET自身的CLR管道運行時中進行的。而且,那些Filter攔截器還只能用於ASP.NET Web Api環境,而不能像本方案這樣用於一般程式。

現在,為了讓前面第三步創建的配置文件生效,我們此時還需要在此主調項目中對它進行鏈接:
若是在.Net Core環境下,我們只需要把DeveloperSharp.json文件放到程式執行目錄中(即bin目錄下與dll、exe等文件的同一目錄中,放錯了位置會報錯)(註意:有些.Net Core版本在Visual Studio“調試”時,不會在bin目錄下生成全部的dll、exe,此時需要把此配置文件放在應用程式的“根目錄”下)。

若是在.Net Framework環境下,我們需要在工程配置文件App.config/Web.config中添加appSettings節點,節點內容如下:

  <appSettings>
    <add key="ConfigFile" value="D:\Test\Assist\DeveloperSharp.xml" />
  </appSettings>

此處需要設置為配置文件的“絕對路徑”(使用“絕對路徑”而不是“相對路徑”,一是有利於安全性,二是有利於分散式部署)

 

一切準備完畢,運行,結果如下:

【控制台顯示出】:共有老師20人

可見AOP已經攔截成功。

若此時,我們在配置文件DeveloperSharp.json/DeveloperSharp.xml中稍做修改,比如:把“GetInfo1”這個方法名改為“ABC”這樣一個不存在的方法名,再運行,結果如下:

【控制台顯示出】:共有學生200人

 

2.Aspect橫切麵編程

上面,第二步,製作的橫切麵程式,是通過修改主程式方法的參數值,而最終改變了主程式的返回值。

其實,我們也有辦法直接修改主程式方法的返回值,比如把上面Interceptor1類的代碼修改為如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {

        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {
            //把主程式的返回值改掉
            e.MethodInfo.ReturnValue = $"共有校長2人";
        }
    }
}

運行,結果如下:

【控制台顯示出】:共有校長2人

 

到目前為止,我們已經知道瞭如何通過“Aspect橫切麵程式”修改主程式方法的參數值、返回值。

如果我們想進一步獲取主程式的“命名空間”、“類名”、“方法名”、“參數名”、“參數類型”、“返回值類型”,則可以通過如下代碼獲取:

e.MethodInfo.NamespaceName                       //命名空間
e.MethodInfo.ClassName                           //類名
e.MethodInfo.MethodName                          //方法名
e.MethodInfo.ParameterInfos[0].Name              //參數名(第一個參數)
e.MethodInfo.ParameterInfos[0].ParameterType     //參數類型(第一個參數)
e.MethodInfo.ReturnValue.GetType()               //返回值類型

 

有時候,在某些特殊情況下,我們希望主程式方法不運行,此時則可以通過在PreProcess方法里把e.Continue設置為false來完成。

接前面的“第一個AOP程式”,比如:我們希望當人數大於10000時,主程式方法就不再運行,則可以通過把Interceptor1類的代碼修改為如下樣式來實現:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //當人數大於10000時,主程式方法就不再運行
            if (Convert.ToInt32(e.MethodInfo.ParameterValues[1]) > 10000)
                e.Continue = false;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

現在的這個示例是一個Aspect橫切麵程式攔截一個主程式。在後續將要講解的“多個Aspect橫切麵程式攔截一個主程式”的情況中,只要有一個e.Continue=false被設置,主程式方法就不會運行(在此事先提點)。

 

3.一個橫切麵程式攔截多個主程式

為了演示這部分的內容,我們首先在前面“第一個AOP程式”的基礎上,把主程式進行擴充。採取的動作是:

(1)在PersonManage類中增加一個GetInfo2方法

(2)再新增一個主程式類SystemManage,該類中有一個名為GetMessage1的方法。代碼如下:

PersonManage類:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class PersonManage : LogicLayer
    {
        public string GetInfo1(string Name, int Num)
        {
            return $"共有{Name}{Num}人";
        }

        public string GetInfo2(string Name, int Num)
        {
            return $"學校共有{Name}{Num}人";
        }
    }
}

SystemManage類:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class SystemManage : LogicLayer
    {
        public string GetMessage1(string Name1, int Num1, string Name2, int Num2)
        {
            return $"第一組共有{Name1}{Num1}人,第二組共有{Name2}{Num2}人";
        }
    }
}

如此一來,現在就有了3個主程式方法。

 

接下來,我們修改配置文件,讓Interceptor1去攔截上述的3個主程式方法。

若是在.Net Core環境下,DeveloperSharp.json文件的內容修改為如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.PersonManage",
           "method":"*"   //星號*代表該作用域下的全部方法
         },
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.SystemManage",
           "method":"GetMessage1"
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容修改為如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="*"/>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.SystemManage" method="GetMessage1"/>
  </AspectObject>
</DeveloperSharp>

 

 最後,我們把控制台啟動程式修改為如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
            var pm = new School.Logic.PersonManage();
            var sm = new School.Logic.SystemManage();

            //要用這種形式調用主程式中的方法,AOP功能才會生效
            var str1 = pm.InvokeMethod("GetInfo1", "學生", 200);
            var str2 = pm.InvokeMethod("GetInfo2", "學生", 200);
            var str3 = sm.InvokeMethod("GetMessage1", "學生", 200, "院士", 10);
            Console.WriteLine(str1);
            Console.WriteLine(str2);
            Console.WriteLine(str3);

            Console.ReadLine();
        }

運行結果如下:

【控制台顯示出】:

共有老師20人
學校共有老師20人
第一組共有老師20人,第二組共有院士10人

可見AOP所有攔截均已成功!

 

4.多個橫切麵程式攔截一個主程式

為了演示這部分的內容,我們還是要先回到前面的“第一個AOP程式”,在它的基礎上,我們新增一個名為Interceptor2的Aspect橫切麵類,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor2 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的兩個參數值改掉
            e.MethodInfo.ParameterValues[0] = "輔導員";
            e.MethodInfo.ParameterValues[1] = 40;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

如此一來,我們就有了2個Aspect橫切麵程式Interceptor1與Interceptor2。

 

接下來,我們修改配置文件,讓Interceptor1、Interceptor2都去攔截主程式方法GetInfo1。

若是在.Net Core環境下,DeveloperSharp.json文件的內容修改為如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.PersonManage",
           "method":"GetInfo1"
         },
         {
           "name":"School.Aspect.Interceptor2", 
           "scope":"School.Logic.PersonManage",
           "method":"GetInfo1"
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容修改為如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="GetInfo1"/>
    <Ao name="School.Aspect.Interceptor2" scope="School.Logic.PersonManage" method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

 

上述修改完畢,運行控制台主調程式,結果如下:

【控制台顯示出】:共有輔導員40人

從上述運行結果,我們大致可以推斷出:Interceptor1、Interceptor2這兩個Aspect橫切麵攔截器是按配置順序執行的。其中,Interceptor1先把GetInfo1方法的兩個參數值改為了("老師",20),接著,Interceptor2又把GetInfo1方法的兩個參數值改為了("輔導員",40),所以最終GetInfo1方法的參數值變為了("輔導員",40)。

 

5.AOP的泛型處理
如果我們的主程式是泛型方法,則需要用InvokeMethod<T>這種方式來進行調用。

比如,現有如下的主程式代碼:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace Test4Logic
{
    //主程式必須繼承自LogicLayer類
    public class Calculate : LogicLayer
    {
        public int add(int i, int j)
        {
            return i + j;
        }

        public int add(int i, int j, int k)
        {
            return i + j + k;
        }

        public string add<T>(T i, T j, T k)
        {
            return "T" + i.ToString() + j.ToString() + k.ToString();
        }

        public string add<T, V>(T i, T j, V k)
        {
            return "TTV" + i.ToString() + j.ToString() + k.ToString();
        }

        public string add<T, V>(T i, V j, V k)
        {
            return "TVV" + i.ToString() + j.ToString() + k.ToString();
        }
    }
}

 

對應的Aspect橫切麵類代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace Test4Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的第一個參數值改掉
            e.MethodInfo.ParameterValues[0] = 8;

        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

 

對應的配置文件如下:
若是在.Net Core環境下,DeveloperSharp.json文件的內容如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"Test4Aspect.Interceptor",  //橫切麵攔截器類
           "scope":"Test4Logic.Calculate",    //被攔截的主程式類
           "method":"add"                     //被攔截的方法
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="Test4Aspect.Interceptor" scope="Test4Logic.Calculate" method="add"/>
  </AspectObject>
</DeveloperSharp>

 

控制台主調程式代碼如下: 

        //需要引用Test4Aspect、Test4Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
              var cal = new Test4Logic.Calculate();

              //要用這種形式調用主程式中的方法,AOP功能才會生效
              var r1 = cal.InvokeMethod("add", 1, 2);
              var r2 = cal.InvokeMethod("add", 1, 2, 3);
              var r3 = cal.InvokeMethod<int>("add", 1, 2, 3);
              var r4 = cal.InvokeMethod<int, float>("add", 1, 2, (float)3);
              var r5 = cal.InvokeMethod<int, float>("add", 1, (float)2, (float)3);

              Console.WriteLine(r1);
              Console.WriteLine(r2);
              Console.WriteLine(r3);
              Console.WriteLine(r4);
              Console.WriteLine(r5);

              Console.ReadLine();
        }

 

運行上述控制台主調程式,結果如下:

【控制台顯示出】:
10
13
T823
TTV823
TVV823

主程式中每個泛型方法的對應調用一目瞭然。

 

6.AOP的非同步處理
如果我們的主程式是非同步方法,還是使用InvokeMethod來進行調用。下麵給出一個代碼樣式示例(代碼做了簡化處理):

//主程式

    //主程式必須繼承自LogicLayer類
    public class UserService : LogicLayer
    {
        public async Task<Worker> GetUser(string Id, int Age, string Name)
        {
              //...相關代碼...
        }

        public async Task<T> GetUser<T>(string Id, int Age, string Name) where T : User, new()
        {
              //...相關代碼...
        }
    }
----------------------------------------------------------------------
//主調程式

       var us = new UserService();

       //要用這種形式調用主程式中的方法,AOP功能才會生效
       var worker = await us.InvokeMethod("GetUser", "C007", 26, "alex");
       var user = await us.InvokeMethod<Manager>("GetUser", "A002", 46, "kevin");

       Console.WriteLine(worker.Name);
       Console.WriteLine(user?.Name);

 

即然主程式可以是非同步的,那Aspect橫截面攔截程式能不能也是非同步的了?答案是肯定的。你可以把PreProcess與PostProcess中的至少一個改為非同步方法,實現單個Aspect類的同步非同步混用,其代碼結構與原先的同步Aspect類一致,這點連.NET/微軟自身都還沒有實現...
下麵給出一個示例代碼:

    //橫切麵程式必須繼承自AspectModel類
    public class UserInterceptor : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {

        }

        //PostProcess方法後於主程式執行
        public override async void PostProcess(object sender, AspectEventArgs e)
        {
            await Task.Run(() =>
            {
                Thread.Sleep(10000);
                File.AppendAllText("D:/zzz.txt", "耗時操作");
            });
        }
    }

  

7.優勢總結

本文所講述的,是全網唯一實現AOP徹底解耦的技術方案。使用它,當你需要給主程式增加Aspect橫切麵攔截器時,無論是增加一個還是多個,都不再需要修改&重新編譯主程式。這實現了不同功能構件之間的0依賴拼裝/拆卸開發方式,隨之而來的也會對研發團隊的管理模式產生重大影響,意義深遠...

 

8.展望

AOP對於程式代碼的解耦、業務模塊的拆分與拼裝組合,有著巨大的作用。正確的使用AOP,甚至能對傳統的軟體架構設計,產生顛覆性的影響,如超級戰士出場一般,讓所有人刮目相看,完全耳目一新!!

為了讓讀者能直觀感知AOP的上述神奇魅力,下麵給出一個業務案例:
有一批貨品要錄入資料庫,貨品包含長、寬、高、顏色、類型等屬性。現在有業務需求如下,
(1)當貨

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 強制等待 即sleep()方法,由python中的time模塊提供,強制讓代碼等待xxx時間,無論前面的代碼是否執行完成或者還未完成,都必須等待設定的時間。 示例代碼如下: # coding = utf-8 from selenium import webdriver from time impor ...
  • 拓展閱讀 MySQL View MySQL truncate table 與 delete 清空表的區別和坑 MySQL Ruler mysql 日常開發規範 MySQL datetime timestamp 以及如何自動更新,如何實現範圍查詢 MySQL 06 mysql 如何實現類似 oracl ...
  • maku-generator —— 一款低代碼生成器,可根據自定義模板內容,快速生成前後端代碼,可實現項目的快速開發、上線,減少重覆的代碼編寫,開發人員只需專註業務邏輯即可。 ...
  • 目錄簡介源碼函數說明arv_camera_newarv_camera_acquisitionarv_camera_get_model_namearv_buffer_get_image_widtharv_buffer_get_image_height 簡介 本文針對官方常式中的第一個常式:single ...
  • DDD 領域驅動設計理解(Domain Driven Design) 目錄DDD 領域驅動設計理解(Domain Driven Design)概念核心目標 概念 領域驅動設計事實上是1針對OOAD的一個擴展和延申。DDD基於面向對象分析與設計技術。 對技術架構進行了分層規劃。 對每個類進行了策略和劃 ...
  • Spring Boot 允許你將配置外部化,以便可以在不同的環境中使用相同的應用程式代碼。可以使用屬性文件、YAML文件、環境變數和命令行參數將配置外部化。屬性值可以通過使用 @Value 註解直接註入 bean,可以通過 Spring 的 Environment 抽象訪問,也可以通過 @Confi... ...
  • 永久激活支持全家桶所有軟體,包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 下麵以 Intellij IDEA 作為演示。 準備工作:下載插件包 https://qweree.cn/index.php/259/(如果 ...
  • 本文介紹基於Python中ArcPy模塊,實現Excel數據讀取並生成矢量圖層,同時進行IDW插值與批量掩膜的方法。 1 任務需求 首先,我們來明確一下本文所需實現的需求。 現有一個記錄有北京市部分PM2.5濃度監測站點在2019年05月18日00時至23時(其中不含19時)等23個逐小時PM2.5 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...