解决Xamarin.Android绑定第三方库时类型丢失的问题(二)

在Crasheye的SDK时,我再一次遇到了绑定问题,之前的问题请看解决Xamarin.Android绑定第三方库时类型丢失的问题(一),这一次出现的问题更多,也更棘手,其中几条查阅官方文档也没有发现解决方案。
问题是这样的:


下面我们一条一条来看,第一条问题报告如下:
CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Base.BaseDao.cs(29,29): Error CS0111: Type 'BaseDao' already defines a member called 'Delete' with the same parameter types (CS0111) (CrashEyeTestPlus)

查看问题源,发现文件中生成了两个一模一样的方法,连参数也是一样的:


    // Metadata.xml XPath method reference: path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]"
        [Register ("delete", "(Ljava/io/Serializable;)I", "GetDelete_Ljava_io_Serializable_Handler")]
        public virtual unsafe int Delete (global::Java.Lang.Object id)
        {......}

          ........

        // Metadata.xml XPath method reference: path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='T']]"
        [Register ("delete", "(Ljava/lang/Object;)I", "GetDelete_Ljava_lang_Object_Handler")]
        public virtual unsafe int Delete (global::Java.Lang.Object entity)
        {.......}

为了搞清楚怎么回事,我还是和上次一样,在AS里查看一下原生的接口是什么样子:

public abstract class BaseDao<T, ID extends Serializable> implements YoDao<T, ID> {
    .....
    public int delete(ID id) {
        return this.deleteByFields(this.whereClauseByPK(), this.whereArgsByPK(id));
    }

    public int delete(T entity) {
        int count = 0;
        if (entity != null) {
            ID id = this.getPK(entity);
            if (id != null) {
                count = this.delete(id);
            }
        }

        return count;
    }
    .....
}

可见T和ID都是泛型,C#对Java的泛型支持并不太好,所以Xamarin在封装的时候,把T和ID都当成了Object类型对待,因此导致两个方法封装成了一模一样的外观。为了令两个方法能相互区别,我将delete(ID id) 的参数类型修改为Serializable类型(ID本来就是一个Serializable)。在Metedata.xml文件中加入如下代码:
<attr path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]/parameter[1]" name="managedType">Java.IO.ISerializable</attr>

第二个问题是CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Base.IYoDao.cs(7,7): Error CS0111: Type 'IYoDao' already defines a member called 'Delete' with the same parameter types (CS0111) (CrashEyeTestPlus)

这个问题的原因和上面是一样的,BaseDao的delete方法就是对IYoDao接口的实现,

public interface YoDao<T, ID extends Serializable> {
      ......
    int delete(ID var1);
    int delete(T var1);
      .......
}

所以我们同样把delete(ID var1)的参数类型修改过来即可。在Metedata.xml文件中加入如下代码:

<attr path="/api/package[@name='com.xsj.crasheye.dao.base']/interface[@name='YoDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]/parameter[1]" name="managedType">Java.IO.ISerializable</attr>

第三个问题是 CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(23,23): Error CS0534: 'SessionDaoImpl' does not implement inherited abstract member 'BaseDao.SetPK(Object, Object)' (CS0534) (CrashEyeTestPlus)
查看一下生成的SessionDaoImpl文件,能够看到

    public partial class SessionDaoImpl : global::Com.Xsj.Crasheye.Dao.Base.BaseDao, global::Com.Xsj.Crasheye.Dao.ISessionDao {

      ......
    public virtual unsafe global::Com.Xsj.Crasheye.Session.Session SetPK (global::Com.Xsj.Crasheye.Session.Session entity, global::Java.Lang.Long id){
      ......
     }
}

其中Com.Xsj.Crasheye.Dao.ISessionDao的原生实现是这样的:

public interface SessionDao extends YoDao<Session, Long> {
}

因此可知,在SessionDaoImpl下T和ID已经被声明为Session和Long,但Xamarin并不知道,他仍要求SessionDaoImpl要有一个BaseDao.SetPK(Object, Object)的实现,但BaseDao并不知道T和ID的具体类型是什么,所以我们只能修改SessionDaoImpl中的SetPK方法的参数类型以匹配BaseDao.SetPK(Object, Object)。在Metedata.xml文件中加入如下代码:

  <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]/parameter[1]" name="managedType">Java.Lang.Object</attr>
    <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]/parameter[2]" name="managedType">Java.Lang.Object</attr>

但问题还没有结束,CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(23,23): Error CS0534: 'SessionDaoImpl' does not implement inherited abstract member 'BaseDao.SetPK(Object, Object)' (CS0534) (CrashEyeTestPlus)仍然还在。再看一下生成的SessionDaoImpl文件,会发现SetPK的声明已经发生改变:public virtual unsafe global::Com.Xsj.Crasheye.Session.Session SetPK (global::Java.Lang.Object entity, global::Java.Lang.Object id)。问题出在哪里呢?问题出在virtual声明上。
根据C#的语法要求,SessionDaoImpl的SetPK方法要实现BaseDao.SetPK,除了方法名、参数和返回类型要一一匹配之外,方法声明里还要有override,现在是virtual自然是不对的。

首先要移除virtual声明,添加如下代码可以解决:<attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="final">true</attr>,但仅仅是移除virtual是不够的,因为Java的成员方法默认是虚方法,都是可以重写的,而C#的成员方法则默认是非虚的,是不可重写的,因此override在这里是必须的。
但官方文档并没有给出这种情况应该如何解决,我经过一番摸索,得出了下述的解决方案:
<attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="visibility">public override</attr>将override关键字写在可见性声明里居然也可以,可见Metadata.xml本质就是一个模板文件;
进行到这一步,问题又变化了CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(67,67): Error CS0508: 'SessionDaoImpl.SetPK(Object, Object)': return type must be 'Object' to match overridden member 'BaseDao.SetPK(Object, Object)' (CS0508) (CrashEyeTestPlus),SessionDaoImpl.SetPK方法的返回类型不匹配。这个简单,添加如下代码即可:
<attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="managedReturn">Java.Lang.Object</attr>
至此,关于SessionDaoImpl.SetPK的问题才算完全解决,但显然,SessionDaoImpl.SetPK方法的所有泛型的类型信息都被擦除了,我们在调用的时需要小心才行,保险起见,我们可以再封装一下SessionDaoImpl。

其他的问题的原因和解决方案与上述大同小异,这里就不再赘述了,希望本文能对你有帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容