C# 程式集

来源:http://www.cnblogs.com/jehorn/archive/2017/10/27/7744320.html
-Advertisement-
Play Games

一、概述 1. 程式集是.NET應用程式的部署單元 程式集是.NET應用程式的部署單元。 程式集是自我描述的安裝單元,由一個或多個文件組成。 通常擴展名是EXE或DLL的.NET可執行程式稱為程式集。 .NET程式集包含元數據。 2. 程式集的特性 程式集是自我描述的。 版本的相互依賴性在程式集的清 ...


一、概述
  1. 程式集是.NET應用程式的部署單元

    程式集是.NET應用程式的部署單元。
    程式集是自我描述的安裝單元,由一個或多個文件組成。
    通常擴展名是EXE或DLL的.NET可執行程式稱為程式集。
    .NET程式集包含元數據。

  2. 程式集的特性
    • 程式集是自我描述的。
    • 版本的相互依賴性在程式集的清單中記錄。
    • 程式集可以並行載入。
    • 應用程式使用應用程式域來確保其獨立性。
    • 安裝簡單。
  3. 程式集的結構

    程式集由描述它的元數據、描述導出類型和方法的類型元數據、MSIL代碼和資源組成。

  4. 程式集的清單
    程式集清單是元數據的一部分,描述了程式集和引用它所需要的所有信息和依賴關係。
    • 標識(名稱、版本、文化和公鑰)。
    • 屬於該程式集的一個文件列表。
    • 引用程式集的列表。
    • 一組許可請求——運行這個程式集所需要的許可。
    • 導出的類型。
  5. 命名空間和程式集

    命名空間完全獨立於程式集。
    在一個程式集中可以有不同的命名空間,一個命名空間也可以分佈在多個程式集中。
    命名空間是類名的一種擴展,屬於類名範疇。

  6. 私有和共用程式集
    私有程式集在應用程式所在目錄下或子目錄中。
    私有程式集需要註意命名衝突。
    使用共用程式集時,需要註意:程式集必須是唯一的,有強名(唯一的名稱,強制的版本號)

  7. 輔助程式集
    輔助程式集是只包含資源的程式集,它尤其適用於本地化。

  8. 查看程式集
    ildasm,這是一個MSIL反彙編程式。命令行運行ildasm,把程式集作為其參數或File|Open菜單。
    還有.NET Reflector是用於分析程式集的工具。

二、構建程式集
  1. 創建模塊和程式集

    模塊是一個沒有程式集特性的DLL。

    csc /target:module hello.cs
    創建模塊hello.netmodule
    ildasm hello.netmodule查看這個模塊。

    /addmodule選項,可以把模塊添加到現有的程式集中。

    創建一個A.cs類文件,然後csc /target:module A.cs編譯,生成了A.netmodule文件,它不包括程式集的信息;下麵生成一個程式集B,它包括模塊A.netmodule,命令如下:csc /target:library /addmodule:A.netmodule /out:B.dll,然後使用IL查看程式集:ildasm B.dll;如圖所示:
    B.dll

    在使用IL查看程式集時,只能找到一個清單。

    模塊的作用是更快地啟動程式集,因為不是所有類都在一個文件中,模塊只在需要時載入。另外可以使程式集可以使用多種編程語言來創建。

  2. 程式集的屬性
    • ./Solution Explorer/Properties/AssemblyInfo.cs文件,可以使用一般的源代碼編輯器配置程式集的屬性。
    • assembly首碼把屬性標記為全局屬性。
    • 程式集的全局屬性與特定的語言元素無關,用於程式集屬性的參數是命名空間System.ReflectionSystem.Runtime.Compiler ServicesSystem.Runtime.InteropServices中的類。
    • System.Reflection命名空間中定義的程式集屬性列表:

    System.Reflection命名空間中定義的程式集屬性
    在VS中,可以右鍵項目 - 屬性 - 應用程式設置 - 程式集信息 來配置這些屬性:

    設置程式集屬性1

    設置程式集屬性2

  3. 動態載入和創建程式集
    在開發期間,添加對程式集的引用,該程式集中的類型就可用於編譯器。
    可以變成載入程式集。為此可以使用類Assembly的靜態方法Load(),這個方法是重載的,可以使用AssemblyName給它傳送程式集的名稱或位元組數組。
    例子:使用WPF,輸入代碼,點擊按鈕執行並將結果顯示到TextBlock中。
    UI

    創建類CodeProvider,定義方法CompileAndRun(),編譯文本框中的代碼,啟動所生成的方法。代碼如下:

    using System;
    using System.CodeDom.Compiler;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using Microsoft.CSharp;
    namespace 動態載入和創建程式集
    {
    public class CodeProvider
    {
        private string prefix =
            "using System;" +
            "public static class Driver" +
            "{" +
            "public static void Run()" +
            "{";
    
        private string postfix =
            "}" +
            "}";
        public string CompileAndRun(string input, out bool hasError)
        {
            hasError = false;
            string returnData = null;
    
            CompilerResults results = null;
            using (CSharpCodeProvider provider = new CSharpCodeProvider())
            {
                CompilerParameters options = new CompilerParameters();
                options.GenerateInMemory = true;
    
                StringBuilder sb = new StringBuilder();
                sb.Append(prefix);
                sb.Append(input);
                sb.Append(postfix);
    
                results = provider.CompileAssemblyFromSource(
                    options, sb.ToString());
            }
    
            if(results.Errors.HasErrors)
            {
                hasError = true;
                StringBuilder errorMessage = new StringBuilder();
                foreach (CompilerError error in results.Errors)
                {
                    errorMessage.AppendFormat("{0} {1}", error.Line, error.ErrorText);
                }
    
                returnData = errorMessage.ToString();
            }
            else
            {
                TextWriter temp = Console.Out;
                StringWriter writer = new StringWriter();
                Console.SetOut(writer);
                Type driverType = results.CompiledAssembly.GetType("Driver");
    
                driverType.InvokeMember("Run", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, null);
                Console.SetOut(temp);
    
                returnData = writer.ToString();
            }
    
            return returnData;
        }
    
    }
    }

    按鈕的Click事件連接到Compile_Click方法上:實例化CodeProvider類,調用方法CompileAndRun();從文本框textCode中提取輸入,把結果寫到TextBlock控制項textOutput中:

    private void Compile_Click(object sender, RoutedEventArgs e)
        {
            CodeProvider driver = new CodeProvider();
            bool isError;
            textOutput.Text = driver.CompileAndRun(textCode.Text, out isError);
            if(isError)
            {
                textOutput.Background = Brushes.Red;
            }
        }

    程式運行結果如圖所示:

    程式運行結果

  4. 應用程式域
    .NET的應用程式邊界:應用程式域。

    使用托管IL代碼,運行庫就不能訪問同一個進程中的另一個應用程式的記憶體。多個應用程式可以運行在一個進程的多個應用程式域中。

    AppDomain類用於創建和中斷應用程式域,載入和卸載程式集和類型、枚舉域中的程式集和線程。

    例子:創建控制台程式AssemblyA,在Main()方法中添加Console.WriteLine(),再添加一個類Demo,其構造函數的參數是兩個int值,用AppDomain類創建實例。

    namespace AssemblyA
    {
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main in domain {0} called", AppDomain.CurrentDomain.FriendlyName); ;
            Console.ReadKey();
        }
    }
    }
    namespace AssemblyA
    {
    public class Demo
    {
        public Demo(int val1, int val2)
        {
            Console.WriteLine("Constructor with the values {0}, {1}" + " in domain {2} called", val1, val2, AppDomain.CurrentDomain.FriendlyName);
        }
    }
    }

    運行結果如下圖所示:

    AssemblyA運行結果

    接著創建控制台程式DomainTest,首先使用AppDomain類的FriendlyName屬性顯示當前域的名稱;調用CreateDomain()方法,創建一個新的應用程式域New AppDomain,然後把程式集AssemblyA載入到新域中,通過調用ExecuteAssembly()來調用Main()方法:

    namespace DomainTest
    {
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            Console.WriteLine(currentDomain.FriendlyName);
    
            AppDomain secondDomain = AppDomain.CreateDomain("New AppDomain");
            secondDomain.ExecuteAssembly("AssemblyA.exe");
            /*secondDomain.CreateInstance(
                "AssemblyA",
                "AssemblyA.Demo",
                true,
                System.Reflection.BindingFlags.CreateInstance,
                null,
                new object[] { 7, 3 }, null, null, null);*/
    
            Console.ReadKey();
        }
    }
    }

    啟動程式前先把Assembly.exe複製到當前項目目錄下./bin/Debug,運行結果:
    DomainTest運行結果1.png

    看到第二行New AppDomain中新載入的程式集的輸出結果里看不到AssemblyA.exe的執行,因為沒有創建新的進程;用CreateInstance()替代ExecuteAssembly()方法:它的第一個參數是程式集名稱,第二個參數定義了應實例化的類,第三個參數true表示不區分大小寫,System.Reflection.BindingFlags.CreateInstance是一個綁定標誌枚舉值,指定應調用的構造函數;只需要將上面代碼secondDomain.ExecuteAssembly("AssemblyA.exe");註釋掉,下麵/**/中的代碼取消註釋,運行結果如下:
    DomainTest運行結果2
    在運行期間,主應用程式域會自動創建。ASP.NET為每個運行在Web伺服器上的Web應用程式創建一個應用程式域;Internet Explorer創建運行托管控制項的應用程式域;對於應用程式,卸載程式集只能通過中斷應用程式域來進行

    如果程式集是動態載入的,且需要在使用完後卸載程式集,應用程式域就是非常有用的。在主程式域中,不能刪除已載入的程式集,但可以終止應用程式域,在該應用程式域中載入的所有程式集都會從記憶體中清除出去。

    修改之前的WPF程式:添加一個新類,使用CreateInstanceAndUnwrap()實例化類CodeDriver,調用CompileAndRun()方法,之後再次卸載新應用程式域。

    namespace 動態載入和創建程式集
    {
    public class CodeDriverInAppDomain
    {
        public string CompileAndRun(string code, out bool hasError)
        {
            AppDomain codeDomain = AppDomain.CreateDomain("CodeDriver");
            CodeDriver codeDriver = (CodeDriver)
                codeDomain.CreateInstanceAndUnwrap(
                    "動態載入和創建程式集",
                    "動態載入和創建程式集.CodeDriver");
            string result = codeDriver.CompileAndRun(code, out hasError);
    
            AppDomain.Unload(codeDomain);
    
            return result;
        }
    }
    }

    要在另一個應用程式域中訪問類CodeDriver,類CodeDriver就必須派生於基類MarshalByRefObject。所以CodeDriver.cs修改:

    namespace 動態載入和創建程式集
    {
    public class CodeDriver : MarshalByRefObject
    {
        ...
    }

    按鈕事件處理程式可以修改為使用新類CodeDriverInAppDomain

    private void Compile_Click(object sender, RoutedEventArgs e)
    {
       //CodeDriver driver = new CodeDriver();
       CodeDriverInAppDomain driver = new CodeDriverInAppDomain();
       bool isError;
       textOutput.Text = driver.CompileAndRun(textCode.Text, out isError);
       if(isError)
       {
           textOutput.Background = Brushes.Red;
       }
    }

    使用AppDomain類的GetAssemblies()方法,可以查看應用程式域中載入的程式集。

  5. 共用程式集
    共用程式集必須有一個強名
    共用程式集必須有一個強名,來唯一地標識該程式集。
    強名由以下組成:
    • 程式集本身的名稱
    • 版本號
    • 公鑰
    • 文化

為了唯一地標識公司中的程式集,應使用命名空間層次結構來給類命名。

查看全局程式集緩存:在`C:\Windows\assembly\ `  目錄下 。
程式集查看器可以使用Windows資源管理器查看和刪除程式集。`gacutil.exe`工具可以使用命令行安裝、卸載和顯示程式集。

**創建共用程式集**

建立一個Visual C# Class Library項目SharedDemo;命名空間Wrox.ProCSharp.Assemblies.Sharing,類名SharedDemo
類的構造函數把文件的所有行都將讀取到一個集合中;文件名作為參數傳送給構造函數;方法GetQuoteOfTheDay()只返回這個集合的一個隨機字元串。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Wrox.ProCSharp.Assemblies.Sharing
{
    public class SharedDemo
    {
        private List<string> quotes;
        private Random random;

        public SharedDemo(string filename)
        {
            quotes = new List<string>();
            Stream stream = File.OpenRead(filename);
            StreamReader streamReader = new StreamReader(stream);
            string quote;
            while ((quote = streamReader.ReadLine()) != null)
            {
                quotes.Add(quote);
            }
            streamReader.Close();
            stream.Close();
            random = new Random();
        }

        public string GetQuoteOfTheDay()
        {
            int index = random.Next(1, quotes.Count);
            return quotes[index];
        }
    }
}

要共用這個程式集,需要一個強名,使用強名工具(sn):sn -k mykey.snk,強名工具生成和編寫一個公鑰/私鑰對,並把改密鑰對寫到文件中。
在VS中,可以選擇簽名(signing)選項卡,用項目屬性標記程式集。
簽名(signing)
設置了signing選項後,重新建立文件後,公鑰就在程式集清單中。
程式集清單

**安裝共用程式集**

程式集中有了公鑰後就可以使用全局程式集緩存工具gacutil及其/i選項把它安裝到全局程式集緩存中:
gacutil /i SharedDemo.dll
用VS配置以後建立的事件命令行,就可以在全局程式集緩存中安裝每個成功建立的程式集。

未完待續 - 2017年1月25日11:22:28 - P.440


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

-Advertisement-
Play Games
更多相關文章
  • .Net常用類庫 一、String成員方法(常用) 1,bool Contains(string str) 判斷字元串對象是否包含給定的內容 2,bool StartsWith(String str):判斷字元串對象是否以給定的字元串開始。 3,bool EndsWith(String str):判 ...
  • 前言作為一名合格的furry,我不僅要吸娜娜奇,還要天天泡在fa吸大觸們的furry作品,這其中難免遇到某個十分喜愛的作者,於是便想down空此作者的所有作品。鑒於一張張的保存實在費時費力,寫個爬蟲來解決眼前的問題似乎再好不過了,所以便有了現在這個下載器。功能介紹根據作者名批量下載此作者的所有作品,... ...
  • 這是今天幫 "檸檬" 分析一個 "AsyncLocal相關的問題" 時發現的. 試想這個代碼輸出的值是多少? 答案是123. 為什麼修改了 的值卻無效呢? 這要從AsyncLocal的運作機制說起. 首先這是 "AsyncLocal的源代碼" : 獲取和設置值用的是 和`ExecutionConte ...
  • WCF系統內置綁定列表 編碼格式 一個綁定,適用於與符合 WS-Basic Profile的Web服務(例如基於 ASP.NET Web 服務(ASMX)的服務)進行的通信。 此綁定使用HTTP作為傳輸協議,並使用文本/XML作為預設的消息編碼 Text, MTOM WCF各系統綁定所支持的功能 ...
  • 在 "上一篇" 我們對CoreCLR中的JIT有了一個基礎的瞭解, 這一篇我們將更詳細分析JIT的實現. JIT的實現代碼主要在 "https://github.com/dotnet/coreclr/tree/master/src/jit" 下, 要對一個的函數的JIT過程進行詳細分析, 最好的辦法 ...
  • ASP.NET Core 2.0 開源Git HTTP Server,實現類似 GitHub、GitLab。 GitHub:https://github.com/linezero/GitServer 設置 需要先安裝Git,並確保git 命令可以執行,GitPath 可以是 git 的絕對路徑。 目 ...
  • 一、關於分部視圖(Partial View) Partial View是可以應用在View中的,編寫一次,在其他View中可以被反覆使用。通常都是放在"Views——Shared"文件夾中。 1. 創建Partial View:右鍵"Views——Shared"文件夾添加分部視圖。 2. 使用Par ...
  • 最近有不少網友提起await和async,呵呵,C# 5引進的語法糖。 這個語法糖還真不好吃,能繞倒一堆初學的朋友,在網上也有很多網友關於這塊知識點的爭論,有對有錯,今天在這裡把這個誤區好好講講。 在await(C# 參考)這樣寫道: “await 運算符應用於非同步方法中的任務,在方法的執行中插入掛 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...