真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

C++中怎么利用反射調(diào)用.NET的方法-創(chuàng)新互聯(lián)

C++中怎么利用反射調(diào)用.NET的方法,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

創(chuàng)新互聯(lián)公司專注于企業(yè)成都全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、河曲網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5建站、購(gòu)物商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為河曲等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。

使用非泛型集合的委托方法

先看看.NET類中的一個(gè)返回列表數(shù)據(jù)的方法:

//返回List或者數(shù)組,不影響 C++調(diào)用
 public List GetUsers(string likeName)
 {
 List users = new List();
 for (int i = 0; i < 10; i++)
 {
 IUserInfo userinfo = GetUserByID(i);
 userinfo.Name += likeName;
 users.Add(userinfo);
 }
 //return users.ToArray();
 return users;
 }
 public IUserInfo GetUserByID(int userId)
 {
 IUserInfo userinfo= EntityBuilder.CreateEntity();
 userinfo.ID = userId;
 userinfo.Name = "姓名_" + userId;
 userinfo.Birthday = new DateTime(1980, 1, 1);
 return userinfo;
 }

該方法沒(méi)有什么復(fù)雜業(yè)務(wù)邏輯,就是將傳遞進(jìn)來(lái)的參數(shù)給DTO對(duì)象,創(chuàng)建包含10個(gè)這樣的對(duì)象的列表并返回而已。

對(duì)于 GetUsers方法,我們可以創(chuàng)建下面的委托方法來(lái)綁定:

Func fun;

注意這里使用的是非泛型的 IEnumerable接口,在C++需要使用下面這個(gè)命名空間:

using namespace System::Collections;

那么為何不能使用泛型集合呢?

using namespace System::Collections::Generic;

因?yàn)樵贑++端,沒(méi)有直接引用用戶項(xiàng)目的.NET程序集,并不知道泛型集合類型的具體類型,IUserInfo這個(gè)接口無(wú)法直接訪問(wèn),好在IEnumerable也是繼承 IEnumerable 的,所以可以當(dāng)做非泛型對(duì)象在C++中訪問(wèn),因此創(chuàng)建上面的委托方法是可行的。

C++中的列表對(duì)象list

下面看看完整的C++/CLI反射調(diào)用的代碼:

 std::list GetUsers(String^ likeName)
 {
 //調(diào)用.NET方法,得到結(jié)果
 MethodInfo^ method = dotnetObject->GetType()->GetMethod("GetUsers", BindingFlags::Public | BindingFlags::Instance);
 Func^ fun = (Func^)Delegate::CreateDelegate(Func::typeid, 
 this->dotnetObject, method);
 IEnumerable^ result = fun(likeName);
 std::list cppResult;
 for each (Object^ item in result)
 {
 Func^ entityProp = EntityHelper::EntityCallDelegate(item);
 CppUserInfo user;
 user.ID = (int)entityProp("ID");
 user.Name = (String^)entityProp("Name");
 user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));
 cppResult.push_back(user);
 }
 return cppResult;
 }

在C++中,常常使用 list來(lái)表示一個(gè)列表數(shù)據(jù),例如上面方法中的代碼:

std::list cppResult;

為此C++需要包含以下頭文件:

#include 

要將一個(gè)對(duì)象添加到列表結(jié)尾,像下面這樣調(diào)用即可:

cppResult.push_back(user);

在上一篇中已經(jīng)講述了如何從.NET對(duì)象轉(zhuǎn)換給C++本地結(jié)構(gòu)體,所以這個(gè)轉(zhuǎn)換代碼可以直接拿來(lái)用,綜合起來(lái),要從.NET集合得到C++的列表對(duì)象,像下面這樣使用:

std::list cppResult;
 for each (Object^ item in result)
 {
 Func^ entityProp = EntityHelper::EntityCallDelegate(item);
 CppUserInfo user;
 user.ID = (int)entityProp("ID");
 user.Name = (String^)entityProp("Name");
 user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));
 cppResult.push_back(user);
 }

 C++傳遞集合數(shù)據(jù)給.NET

前面講了從.NET反射調(diào)用獲得一個(gè)集合,看起來(lái)比較容易,但是從C++反射調(diào)用時(shí)候傳遞一個(gè)集合就不容易了。注意,這里傳遞的還是.NET的集合,所以這里需要做3件事情:

1,首先構(gòu)建一個(gè).NET集合對(duì)象;

2,轉(zhuǎn)換C++本機(jī)結(jié)構(gòu)數(shù)據(jù)到.NET集合元素;

3,反射調(diào)用.NET方法,傳遞數(shù)據(jù)過(guò)去。

先看要反射調(diào)用的.NET方法定義:

 public bool SaveUsers(IList users)
 {
 UserDb.AddRange(users);
 return true;
 }

方法非常簡(jiǎn)單,沒(méi)有什么業(yè)務(wù)邏輯,接受一個(gè)列表接口的數(shù)據(jù),然后返回一個(gè)布爾值。

在C++端看來(lái),SaveUsers方法的參數(shù)對(duì)象是一個(gè)泛型集合,但是具體是什么對(duì)象并不知道,所以需要反射出泛型集合的類型,同時(shí)還需要構(gòu)建這樣一個(gè)泛型集合對(duì)象實(shí)例。

在本例中,要得到IUserInfo 這個(gè)泛型集合的類型,可以通過(guò)下面的代碼:

MethodInfo^ method = dotnetObject->GetType()->GetMethod("SaveUsers", BindingFlags::Public | BindingFlags::Instance);
array^ pars = method->GetParameters();
Type^ paraType= pars[0]->ParameterType;
Type^ interfaceType = paraType->GetGenericArguments()[0];

注意上面的代碼中使用了C++/CLI的數(shù)組類型 array^ ,而不是C++標(biāo)準(zhǔn)庫(kù)的數(shù)組,因此不要引用下面的命名空間:

using namespace std;

否則VS會(huì)提示數(shù)組定義缺少參數(shù)。

創(chuàng)建泛型List實(shí)例

我們使用List來(lái)做集合對(duì)象,在C#中,我們可以通過(guò)下面的方式得到List泛型的類型,然后進(jìn)一步創(chuàng)建泛型對(duì)象實(shí)例:

Type t= typeof(List<>);

但是,對(duì)應(yīng)的C++/CLI寫(xiě)法卻無(wú)法通過(guò)編譯:

Type^ t=List<>::typeid;

VS總是提示List缺少類型參數(shù),不過(guò)像下面這樣子是可以的:

Type^ t2= List::typeid;

但是IUserInfo 類型正是我們要?jiǎng)討B(tài)反射的,事先并不知道,所以一時(shí)不知道在C++/CLI中如何構(gòu)建List泛型的具體實(shí)例,MS你不能這么坑好么?

既然無(wú)法直接解決,只好曲線救國(guó)了,通過(guò)類型名字,來(lái)創(chuàng)建類型:

 String^ listTypeName = System::String::Format("System.Collections.Generic.List`1[{0}]", interfaceType->FullName);

可惜,這種方式不成功,只好一步步來(lái)了,先創(chuàng)建基本的List泛型類型:

 String^ listTypeName = "System.Collections.Generic.List`1";
 Type^ listType = System::Type::GetType(listTypeName);

成功,在此基礎(chǔ)上,創(chuàng)建真正的泛型List對(duì)象實(shí)例就可以了,完整代碼如下:

static Type^ CreateGenericListType(Type^ interfaceType)
 {
 //直接這樣創(chuàng)建泛型List不成功:
 // String^ listTypeName = System::String::Format("System.Collections.Generic.List`1[{0}]", interfaceType->FullName);
 String^ listTypeName = "System.Collections.Generic.List`1";
 Type^ listType = System::Type::GetType(listTypeName);
 Type^ generListType = listType->MakeGenericType(interfaceType);
 return generListType;
 }
 static IList^ CreateGenericList(Type^ interfaceType)
 {
 Type^ generListType = CreateGenericListType(interfaceType);
 Object^ listObj = System::Activator::CreateInstance(generListType, nullptr);
 IList^ realList = (IList^)listObj;
 return realList;
 }

在方法 CreateGenericListType得到只是一個(gè)泛型List的類型,但我們并不知道這個(gè)List具體的形參類型,所以這個(gè)泛型List還是無(wú)法直接使用,幸好,泛型List也是繼承自非泛型的IList接口的,所以在 CreateGenericList 方法中將泛型List對(duì)象轉(zhuǎn)換成IList接口對(duì)象,之后就可以愉快的使用List對(duì)象了。

IList^ realList = CreateGenericList(interfaceType);

realList->Add(CurrEntity);//CurrEntity 是interfaceType 類型的動(dòng)態(tài)實(shí)體類

反射靜態(tài)方法

在上一篇中,我們?cè)谝粋€(gè).NET方法中通過(guò)接口動(dòng)態(tài)創(chuàng)建實(shí)體類,用的是下面的方式:

IUserInfo userinfo= EntityBuilder.CreateEntity();

CreateEntity是EntityBuilder的靜態(tài)方法,現(xiàn)在我們需要在C++/CLI中,反射調(diào)用此方法。

為什么要反射創(chuàng)建實(shí)體類?

因?yàn)镃reateGenericList(interfaceType) 創(chuàng)建的是一個(gè)泛型List對(duì)象,要求它的成員是一個(gè)實(shí)體類。

Object^ CreateEntityFromInterface(Type^ interfaceType)
 {
  MethodInfo^ method = this->entityBuilderType->GetMethod("CreateEntity", BindingFlags::Public | BindingFlags::Static);
  MethodInfo^ genMethod = method->MakeGenericMethod(interfaceType);
  Object^ entity = genMethod->Invoke(nullptr, nullptr);
  this->CurrEntity = entity;
  return entity;
 }

注意,由于是反射調(diào)用靜態(tài)方法,并且調(diào)用方法時(shí)候并不需要參數(shù),所以Invoke方法的參數(shù)為空。
在C++/CLI中,用nullptr表示空引用,跟C#的null作用一樣。

反射調(diào)用索引器

SOD實(shí)體類可以通過(guò)索引器來(lái)訪問(wèn)對(duì)象屬性,例如下面的C#代碼:

int id=(int)CurrEntity["ID"];
CurrEntity["Name"]="張三";
string name=(string)CurrEntity["Name"];//張三

下面,我們研究如何通過(guò)索引器來(lái)給實(shí)體類的屬性賦值:

我們定義一個(gè) EntityHelper的C++/CLI類,在中間添加下面的代碼:

private:
 Type^ entityBuilderType;
 MethodInfo^ mset; 
 Object^ _CurrEntity;
 //Action^ idxAction;
 void SetPropertyValue(Object^ entity, MethodInfo^ propMethod, String^ propName, Object^ value)
 {
  array^ paraArr = gcnew array{propName, value};
  propMethod->Invoke(entity, paraArr);
 }
public:
void set(Object^ value)
{
 this->mset = _CurrEntity->GetType()->GetMethod("set_Item", BindingFlags::Public | BindingFlags::Instance);
 //this->idxAction= (Action^)Delegate::CreateDelegate(Action::typeid, _CurrEntity, this->mset);
}
void SetPropertyValue(String^ propName, Object^ value)
{
 this->SetPropertyValue(this->CurrEntity, this->mset, propName, value);
 //參數(shù)類型為 Object的委托,可能沒(méi)有性能優(yōu)勢(shì),反而更慢。
 //this->idxAction(propName, value);
}

對(duì)索引器的訪問(wèn),實(shí)際上就是調(diào)用類的 set_Item 方法,VS編譯器會(huì)給包含索引器的對(duì)象生成這個(gè)方法,一般來(lái)說(shuō)我們會(huì)對(duì)要反射調(diào)用的方法創(chuàng)建一個(gè)委托,但是實(shí)驗(yàn)證明,對(duì)索引器使用委托方法調(diào)用,反而效率不如直接反射調(diào)用,即下面的代碼:

void SetPropertyValue(Object^ entity, MethodInfo^ propMethod, String^ propName, Object^ value)
 {
  array^ paraArr = gcnew array{propName, value};
  propMethod->Invoke(entity, paraArr);
 }

注:C++/CLI 的數(shù)組,也可以通過(guò){ } 進(jìn)行初始化。

一切準(zhǔn)備就緒,下面可以通過(guò)以下步驟提交集合數(shù)據(jù)給.NET方法了:

1,反射.NET方法,獲取參數(shù)的泛型形參類型;

2,創(chuàng)建此泛型形參的泛型List對(duì)象實(shí)例;

3,遍歷C++集合(列表list),將結(jié)構(gòu)數(shù)據(jù)賦值給動(dòng)態(tài)創(chuàng)建的實(shí)體類對(duì)象;

4,添加動(dòng)態(tài)實(shí)體類到泛型List對(duì)象集合內(nèi);

5,反射調(diào)用.NET方法,提交數(shù)據(jù)。

//示例1:直接調(diào)用.NET強(qiáng)類型的參數(shù)方法
 //僅僅適用于有一個(gè)參數(shù)的情況并且要求是泛型類型參數(shù)
 bool SaveUsers(std::list users)
 {
  MethodInfo^ method = dotnetObject->GetType()->GetMethod("SaveUsers", BindingFlags::Public | BindingFlags::Instance);
  array^ pars = method->GetParameters();
  Type^ paraType= pars[0]->ParameterType;
  Type^ interfaceType = paraType->GetGenericArguments()[0];
  IList^ realList = CreateGenericList(interfaceType);
  Object^ userObj = helper->CreateEntityFromInterface(interfaceType);
  for each (CppUserInfo user in users)
  {
  helper->CurrEntity = ((ICloneable^)userObj)->Clone();//使用克隆,避免每次反射
  helper->SetPropertyValue("ID", user.ID);
  helper->SetPropertyValue("Name", gcnew String(user.Name));
  helper->SetPropertyValue("Birthday", Covert2NetDateTime(user.Birthday));
  realList->Add(helper->CurrEntity);
  }
  Object^ result= method->Invoke(dotnetObject, gcnew array{ realList});
  return (bool)result;
 }

使用弱類型集合傳輸數(shù)據(jù)

當(dāng)委托遇到協(xié)變和逆變

看看下面兩個(gè)委托方法,哪個(gè)可以綁定到本文說(shuō)的這個(gè).NET方法:

bool SaveUsers(IList users){ }
Func,bool> fun;
Func,bool> fun2;

很明顯,委托方法 fun2不能綁定,因?yàn)閰?shù)是 in 的,不是方法out的,所以調(diào)用的參數(shù)類型不能使用派生程度更小的類型;

再看看下面這種情況:

List GetUsers(string likeName){ }
Func> fun;
Func fun2;

這里,fun,fun2都可以綁定到方法上,因?yàn)榉盒头椒ǖ男螀⒆鳛榉祷刂?,是out的,可以使用派生程度更小的類型。

這是不是很熟悉的泛型類型的 協(xié)變和逆變?

我們知道,反射的時(shí)候,利用委托綁定要反射的方法,能夠大大提高方法的調(diào)用效率,所以對(duì)于我們的方法參數(shù),如果調(diào)用的時(shí)候無(wú)法獲知具體的類型,從而無(wú)法正確構(gòu)造合適的委托方法,不如退而求其次,讓被調(diào)用的方法參數(shù)采用弱類型方式,這樣就可以構(gòu)造對(duì)應(yīng)的委托方法了。

因此,對(duì)我們.NET方法中的 SaveUsers 進(jìn)行改造:

public bool SaveUsers(IList users)
 {
  UserDb.AddRange(users);
  return true;
 }
 public IUserInfo CreateUserObject()
 {
  return EntityBuilder.CreateEntity();
 }
 public bool SaveUsers2(IEnumerable para)
 {
  var users = from u in para
   select u as IUserInfo;
  return SaveUsers (users.ToList());
 }

這里增加一個(gè)方法 SaveUsers2,它采用IEnumerable ,而不是更為具體的  IList,那么采用下面的方式構(gòu)造方法 SaveUsers2 對(duì)應(yīng)的委托方法就可以了:

MethodInfo^ method = dotnetObject->GetType()->GetMethod("SaveUsers2", BindingFlags::Public | BindingFlags::Instance);
Func^,bool>^ fun2 = 
  (Func^, bool>^)Delegate::CreateDelegate(System::Func^, bool>::typeid,
  this->dotnetObject, method);

這樣要構(gòu)造一個(gè)泛型List就不必像之前的方法那么麻煩了:

System::Collections::Generic::List^ list = gcnew System::Collections::Generic::List;

反射調(diào)用SaveUser2完整的代碼如下:

//示例2:調(diào)用.NET弱類型的參數(shù)方法,以便通過(guò)委托方法調(diào)用
 //構(gòu)建委托方法比較容易,適用于參數(shù)數(shù)量多于1個(gè)的情況,
 bool SaveUsers2(std::list users)
 {
  MethodInfo^ method = dotnetObject->GetType()->GetMethod("SaveUsers2", BindingFlags::Public | BindingFlags::Instance);
  Func^,bool>^ fun2 =
   (Func^, bool>^)Delegate::CreateDelegate(System::Func^, bool>::typeid,
   this->dotnetObject, method);
  Object^ userObj = CreateUserObject();
  System::Collections::Generic::List^ list = gcnew System::Collections::Generic::List;
  for each (CppUserInfo user in users)
  {
  helper->CurrEntity = ((ICloneable^)userObj)->Clone();//使用克隆,避免每次反射
  helper->SetPropertyValue("ID", user.ID);
  helper->SetPropertyValue("Name", gcnew String(user.Name));
  helper->SetPropertyValue("Birthday", Covert2NetDateTime(user.Birthday));
  list->Add(helper->CurrEntity);
  }
  bool result = fun2(list);
  return result;
 }

性能測(cè)試

C++/CLI 反射性能測(cè)試

為了測(cè)試 C++/CLI 反射調(diào)用兩種方案(直接反射調(diào)用,委托方法調(diào)用)的效率,我們循環(huán)1000次測(cè)試,下面是測(cè)試代碼:

NetLibProxy::UserProxy^ proxy = gcnew NetLibProxy::UserProxy("..\\NetLib\\bin\\Debug\\NetLib.dll");
std::list list = proxy->GetUsers("張");
 System::Console::WriteLine("C++ Get List data From .NET function,OK.");
 System::Diagnostics::Stopwatch^ sw = gcnew System::Diagnostics::Stopwatch;
 sw->Start();
 for (int i = 0; i<1000; i++)
 proxy->SaveUsers(list);
 sw->Stop();
 System::Console::WriteLine("1,1000 loop,C++ Post List data To .NET function,OK.use time(ms):{0}",sw->ElapsedMilliseconds);
 sw->Restart();
 for(int i=0;i<1000;i++)
 proxy->SaveUsers2(list);
 sw->Stop();
 System::Console::WriteLine("2,1000 loop,C++ Post List data To .NET function,OK..use time(ms):{0}", sw->ElapsedMilliseconds);

不調(diào)試,直接執(zhí)行:

C++ Get List data From .NET function,OK.
1,1000 loop,C++ Post List data To .NET function,OK.use time(ms):65
2,1000 loop,C++ Post List data To .NET function,OK..use time(ms):48

可見(jiàn),雖然在.NET程序端,我們使用了弱類型的泛型集合,綜合起來(lái)還是反射+委托方法執(zhí)行,效率要高。

所以如果你能夠適當(dāng)對(duì)要調(diào)用的.NET方法進(jìn)行封裝,那么可采用使用弱類型集合傳輸數(shù)據(jù)的方案,否則,就在C++/CLI端多寫(xiě)2行代碼,使用強(qiáng)類型傳輸數(shù)據(jù)的方案。

與.NET直接調(diào)用和反射的性能比較

在本篇的方案中,都是C++反射來(lái)調(diào)用.NET方法的,如果都是在.NET應(yīng)用程序中直接調(diào)用或者反射.NET方法,性能差距有多少呢?

我們模擬文中 C++/CLI的UserProxy,寫(xiě)一個(gè).NET中的 UserProxy:

struct UserStruct
 {
  public int ID;
  public string Name;
  public DateTime Birthday;
 }
 class UserProxy
 {
  User user;
  public UserProxy()
  {
   user = new User();
  }
  public List GetUsers(string likeName)
  {
   List result = new List();
   var list = user.GetUsers(likeName);
   foreach (var item in list)
   {
    UserStruct us;
    us.ID = item.ID;
    us.Name = item.Name;
    us.Birthday = item.Birthday;
    result.Add(us);
   }
   return result;
  }
  public bool SaveUsers(IList users)
  {
   List list = new List();
   IUserInfo userObj = user.CreateUserObject();
   foreach (var item in users)
   {
    IUserInfo currUser = (IUserInfo)((ICloneable)userObj).Clone();
    currUser.ID = item.ID;
    currUser.Name = item.Name;
    currUser.Birthday = item.Birthday;
    list.Add(currUser);
   }
   bool result = user.SaveUsers(list);
   return result;
  }

  Object CreateUserObject()
  {
   MethodInfo method = user.GetType().GetMethod("CreateUserObject", BindingFlags.Public | BindingFlags.Instance);
   Func fun = (Func)Delegate.CreateDelegate(typeof( Func), user, method);
   return fun();
  }
  //反射+委托
  public bool SaveUsers2(IList users)
  {
   MethodInfo method = user.GetType().GetMethod("SaveUsers2", BindingFlags.Public | BindingFlags.Instance);
   Func, bool> fun2 = (Func, bool>)Delegate.CreateDelegate(typeof( System.Func, bool>),
    user, method);
   List list = new List();
   object userObj = CreateUserObject();
   foreach (var item in users)
   {
    IUserInfo currUser = (IUserInfo)((ICloneable)userObj).Clone();
    currUser.ID = item.ID;
    currUser.Name = item.Name;
    currUser.Birthday = item.Birthday;
    list.Add(currUser);
   }
   bool result = fun2(list);
   return result;
  }
}
.Net UserProxy

然后同樣循環(huán)1000此調(diào)用,直接執(zhí)行,看執(zhí)行結(jié)果:

1,1000 loop,.NET Post List data To .NET function,OK.use time(ms):4
2,1000 loop,.NET Reflection Post List data To .NET function,OK.use time(ms):14

看完上述內(nèi)容,你們掌握C++中怎么利用反射調(diào)用.NET的方法的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝各位的閱讀!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。


網(wǎng)站名稱:C++中怎么利用反射調(diào)用.NET的方法-創(chuàng)新互聯(lián)
轉(zhuǎn)載注明:http://weahome.cn/article/ccsies.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部