在C++中反射調用.NET(二)

来源:http://www.cnblogs.com/bluedoctor/archive/2017/02/04/6364025.html
-Advertisement-
Play Games

上一篇在C++中反射調用.NET(一)中,我們簡單的介紹瞭如何使用C++/CLI並且初步使用了反射調用.NET程式集的簡單方法,今天我們看看如何在C++與.NET程式集之間傳遞複雜對象。 ...


反射調用返回覆雜對象的.NET方法

定義數據介面

上一篇在C++中反射調用.NET(一)中,我們簡單的介紹瞭如何使用C++/CLI並且初步使用了反射調用.NET程式集的簡單方法,今天我們看看如何在C++與.NET程式集之間傳遞複雜對象。

先看看.NET程式集的一個返回對象的方法:

 public IUserInfo GetUserByID(int userId)
        {
            IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();
            userinfo.ID = userId;
            userinfo.Name = "姓名_" + userId;
            userinfo.Birthday = new DateTime(1980, 1, 1);

            return userinfo;
        }

其中 IUserInfo是一個用戶信息介面:

using System;

namespace NetLib
{
    public interface IUserInfo
    {
        DateTime Birthday { get; set; }
        int ID { get; set; }
        string Name { get; set; }
    }
}

介面內容很簡單,有int,string,DateTime三種類型的屬性,所以可以把它當做.NET與C++傳遞數據的DTO對象介面。

在方法 GetUserByID 中,有一行代碼:

IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();

EntityBuilder對象是PDF.NET SOD框架中的一個實體構造器,調用CreateEntity方法可以根據一個介面創建一個動態實體類對象,通過這種方式,我們可以不用去關心實體類的構造細節,僅僅關心方法調用的數據介面。在後面的示例中,我們都會通過這種介面對象的方式來傳遞數據。

 

綁定委托方法

下麵我們來看看如何在C++/CLI中反射調用GetUserByID 這個方法。
雖然方法返回的是IUserInfo,但是對於我們的C++程式端來說,它並不知道IUserInfo這個介面對象,因為此介面沒有在C++程式端定義,C++程式也沒用引用它所在的.NET程式集,所以我們在反射調用GetUserByID 方法的時候,只能使用“弱類型”的Object,幸運的是我們調用的是返回值,而不是參數(反過來就不行,後面會有介紹),創建下麵的委托對象是合法的:

Func<int, Object> fun;

詳細的C++/CLI反射代碼如下:

CppUserInfo GetUserByID(int userId)
        {
            //調用.NET方法,得到結果
            MethodInfo^ method = dotnetObject->GetType()->GetMethod("GetUserByID", BindingFlags::Public | BindingFlags::Instance);
            Func<int, Object^>^ fun = (Func<int, Object^>^)Delegate::CreateDelegate(Func<int, Object^>::typeid, this->dotnetObject, method);
            Object^ result = fun(userId);

            //轉換托管類型數據到本機結構體
            Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
            CppUserInfo user;
            user.ID = (int)entityProp("ID");
            user.Name = (String^)entityProp("Name");//  MarshalString((String^)entityProp("Name"));
            user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));
            return user;
        }

在上面的代碼中,通過委托方法調用:

Object^ result = fun(userId);

 

使用SOD DTO 對象

我們得到了.NET程式集的方法返回的DTO對象,但是如何取出它的數據賦值給我們的C++本機代碼呢?
所以這裡涉及到2個問題:
1,從Object對象取出數據;
2,將數據轉換並且賦值給C++本地數據結構

對於第一個問題,我們可以反射DTO對象的屬性,然後跟本地數據介面一一對應,但是,本來我們已經在反射調用方法了,再來一次反射事情就複雜了。
幸好,我們的DTO介面對象它是一個動態創建的SOD實體類對象,由於SOD實體類有類似“字典”的功能,可以通過相關方法進行訪問。

實體類基類的一個方法定義:

public object PropertyList(string propertyFieldName)

我們反射此方法並且綁定一個委托對象來調用它:

        static Func<String^, Object^>^ EntityCallDelegate(Object^ entity)
        {
            //實體類基類的一個方法定義:
            //public object PropertyList(string propertyFieldName)
            Type^ base = entity->GetType()->BaseType;
            MethodInfo^ methodEntity = base->GetMethod("PropertyList", BindingFlags::Public | BindingFlags::Instance);
            Func<String^, Object^>^ funEntity = (Func<String^, Object^>^)Delegate::CreateDelegate(Func<String^, Object^>::typeid, 
entity, methodEntity);
//示例 String^ result = (String^)funEntity("Name"); return funEntity; }

然後,就能像下麵這樣使用了:

Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
int id = (int)entityProp("ID");

 

將.NET對象轉換到C++結構體

在示例中,我們定義了一個CppUserInfo結構體:

struct CppUserInfo
{
    int ID;
    //wstring Name;
    CString Name;
    tm Birthday;
};

托管字元串與本機字元串

這個結構體跟C#版本的介面 IUserInfo對應,但是結構體成員有幾個需要註意的地方:

CString Name;

字元串類型的“名字”成員,要在C++中使用字元串類型,必須在C++文件中包含下麵的頭文件:
如果不是 MFC應用程式,包含下麵這個:

#include <atlstr.h>

否則,需要包含這個頭文件:

#include <cstringt.h> 

 

如果不是使用CString,而是 wstring,那麼需要定義一個方法來實現托管字元串到本機字元串的轉換:

     // 
    //要使用下麵的方法,請先  #include <string> 
    //
    static wstring MarshalString(String ^ s) {
        wstring os;
        const wchar_t* chars =
            (const wchar_t*)(Marshal::StringToHGlobalUni(s)).ToPointer();
        os = chars;
        Marshal::FreeHGlobal(IntPtr((void*)chars));
        return os;
    }

上面的方法申明瞭一個 wchar_t* 類型的指針,在方法結尾必須釋放此指針占用的記憶體,所以這種形式的轉換還是比較麻煩。
有關托管字元串跟C++本機字元串的轉換,可以參考下麵2篇文章:

http://bbs.csdn.net/topics/280024331

http://blog.csdn.net/windren06/article/details/7839985

托管日期與本機日期數據

在C++中表示日期的結構體是 tm,但是需要註意的是 tm的year部分僅能夠表示與1900的差值,所以我們可以寫下麵2個方法來簡單的轉換:

    static tm Convert2CppDateTime(DateTime^ dt)
    {
        tm result;
        result.tm_year = dt->Year - 1900;
        result.tm_mon = dt->Month;
        result.tm_wday = dt->Day;
        return result;
    }

    static DateTime^ Covert2NetDateTime(tm cppDate)
    {
        return gcnew DateTime(
            cppDate.tm_year + 1900, 
            cppDate.tm_mon, 
            cppDate.tm_wday
        );
    }

有了字元串跟日期類型的.NET與C++的相互轉換,基本上就能夠使用.NET的DTO對象了,因為其它數字類型只要類型相容,是可以直接使用的,比如int類型。

轉換到本機結構體

下麵再回來看看 GetUserByID 方法內的對象數據轉換部分:

//轉換托管類型數據到本機結構體
            Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
            CppUserInfo user;
            user.ID = (int)entityProp("ID");
            user.Name = (String^)entityProp("Name");//  MarshalString((String^)entityProp("Name"));
            user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));

現在再看看,採用類似“字典”訪問方式的SOD DTO對象,給C++本地結構體轉換賦值數據,就很方便了,這也是本篇選擇SOD框架作為C++與.NET通信的原因了。

為何不使用序列化的問題

在進行分散式跨平臺調用的時候,序列化常常作為一個有效手段被大量使用,但是我們的應用有幾個特點:
1,沒有分散式,在進程內進行不同語言平臺調用;
2,不知道反序列化的類型,因為C++沒有直接引用任何.NET框架自身之外的.NET程式集;
3,序列化需要使用反射,而我們本來已經在反射了,會加重負擔;

除此之外,使用序列化還會有額外的工作:
4,使用序列化會要求被調用端進行額外的封裝;
5,雙方需要制定通用的通信協議,並且定製序列化過程,比如常見RPC框架約定的序列化協議

所以,經過仔細考慮後,放棄了使用序列化方式來進行C++與.NET進行進程內通信的想法。

 

下一篇,我們將介紹C++與.NET如何傳遞集合對象的問題。
(未完待續)

 


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

-Advertisement-
Play Games
更多相關文章
  • 目錄 前言 什麼是ntko 準備工作 實戰演練 總結 一、前言 Web開發中經常需要用到線上處理office文檔的功能,現在市面上有一些常用的Web頁面調用顯示Office的控制項技術,用起來很方便。有一些第三方ActiveX瀏覽器控制項:比如科瀚的SOAOffice中間件、卓正軟體的pageoffic ...
  • 就簡單實現一下卡拉OK的字幕效果 * 字的顏色變化是線性變化,即隨時間的線性變化 而不是按字的單位變顏色的 所以有時候是字的一部分變顏色 主要代碼實現如下: Graphics g = pea.Graphics; string str = "信號ねぇ あるわけねぇ 俺ら村には電気がねぇ!"; Pen ...
  • 通常會增加管理員的區域使項目層次更加清晰 1.右鍵“解決方案”》“增加區域”,然後輸入區功能變數名稱字如“Admin” 2.打開新增區域的路由配置,增加namespace參數 public override void RegisterArea(AreaRegistrationContext context) ...
  • interface IState { string Name { get; set; } //後件處理 IList Nexts { get; set; } Func Selector { get; set; } } class State : IState { pu... ...
  • Repeater控制項是一個數據綁定容器控制項,它能夠生成各個項的列表,並可以使用模板定義網頁上各個項的佈局。當該頁運行時,該控制項為數據源中的每個項重覆此佈局。 配合模板使用repeater控制項 若要使用repeater控制項,需創建定義控制項內容佈局的模板。模板可以包含標記和控制項的任意組合。如果未定義模板 ...
  • "ABP入門系列目錄——學習Abp框架之實操演練" "源碼路徑:Github LearningMpaAbp" 完成了簡單的增刪改查和分頁功能,是不是覺得少了點什麼? 是的,少了許可權管理。既然涉及到了許可權,那我們就細化下任務清單的功能點: 登錄的用戶才能查看任務清單 用戶可以無限創建任務並分配給自己, ...
  • 泛型不同參數類型生成的對象是相互獨立的。 //如 Tuple ts; Tuple to; //ts to 是兩個類型的對象。 很多時候,我們希望實現 to = ts 這種操作,為什麼?因為看上去它應該如此。 為了達到這個目的,就要解決“泛型參數轉換的問題”,這個問題的知識點是in out 泛型變體。... ...
  • lambda 傳遞ref參數有個語法bug,必須要顯式書寫參數類型。 //如 delegate bool FuncType(ref int num); FuncType func1; func1 = num => true; //錯 func1 = (ref num) => true;//錯 fun... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...