在C++中反射调用.NET的方法(三)

2020-01-06 16:54:54于丽

使用弱类型集合传输数据

当委托遇到协变和逆变

看看下面两个委托方法,哪个可以绑定到本文说的这个.NET方法:


bool SaveUsers(IList<IUserInfo> users){ }
Func<List<IUserInfo>,bool> fun;
Func<List<Object>,bool> fun2;

很明显,委托方法 fun2不能绑定,因为参数是 in 的,不是方法out的,所以调用的参数类型不能使用派生程度更小的类型;

再看看下面这种情况:


List<IUserInfo> GetUsers(string likeName){ }
Func<string,IEnumerable<IUserInfo>> fun;
Func<string,IEnumerable> fun2;

这里,fun,fun2都可以绑定到方法上,因为泛型方法的形参作为返回值,是out的,可以使用派生程度更小的类型。

这是不是很熟悉的泛型类型的 协变和逆变?

我们知道,反射的时候,利用委托绑定要反射的方法,能够大大提高方法的调用效率,所以对于我们的方法参数,如果调用的时候无法获知具体的类型,从而无法正确构造合适的委托方法,不如退而求其次,让被调用的方法参数采用弱类型方式,这样就可以构造对应的委托方法了。

因此,对我们.NET方法中的 SaveUsers 进行改造:


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

这里增加一个方法 SaveUsers2,它采用IEnumerable<Object> ,而不是更为具体的  IList<IUserInfo>,那么采用下面的方式构造方法 SaveUsers2 对应的委托方法就可以了:


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

这样要构造一个泛型List就不必像之前的方法那么麻烦了:


System::Collections::Generic::List<Object^>^ list = gcnew System::Collections::Generic::List<Object^>;

反射调用SaveUser2完整的代码如下:


//示例2:调用.NET弱类型的参数方法,以便通过委托方法调用
 //构建委托方法比较容易,适用于参数数量多于1个的情况,
 bool SaveUsers2(std::list<CppUserInfo> users)
 {
  MethodInfo^ method = dotnetObject->GetType()->GetMethod("SaveUsers2", BindingFlags::Public | BindingFlags::Instance);
  Func<System::Collections::Generic::IEnumerable<Object^>^,bool>^ fun2 =
   (Func<System::Collections::Generic::IEnumerable<Object^>^, bool>^)Delegate::CreateDelegate(System::Func<Collections::Generic::IEnumerable<Object^>^, bool>::typeid,
   this->dotnetObject, method);
  Object^ userObj = CreateUserObject();
  System::Collections::Generic::List<Object^>^ list = gcnew System::Collections::Generic::List<Object^>;
  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;
 }