关于在c#和Java中使用“引用”的讨论

6 篇文章 0 订阅
4 篇文章 0 订阅

最近做的项目常常涉及到架构,为了提高代码的复用性,需要在父类引用子类的方法。项目的服务器端用c#,客户端使用java编程,都没有方便明确的使用指针的方式(习惯c++的小伙伴对此哭了好几回),所以都用了比较新的解决方法,对此做个小结。

在BO(business object)层如果有多个实体,常采用写抽象类和继承的方式实现代码的复用。也就是你有一个抽象的父类AEntity有一些公共的方法和成员变量,子类有MyA,MyB,MyC分别对应数据库的一张表,并且在子类还需要维护一个列表以映射子类成员和对应数据库表的属性。当BO层和DC(database control)要交互的时候,复用的代码就要解决实体成员变量值到数据库的交互问题。

如果分别在MyA,MyB,MyC中写set,get代码然后到AEntity里调用再做必要的强转会生成许多不必要的实体,至少代码上看着是让人不爽的(要有架构上的洁癖!),所以我们在写C#的时候采取代理(delegate)的方式来获取函数引用,从而在AEntity写一个方法就能解决所有子类的需求。

如何实现C++指针那般的功能?首先在父类,声明两个代理类型:

 public delegate void DelegateSetValue<T>(T t);
 public delegate T DelegateReadValue<T>();

采用模板的原因是set,get的值可能是不同类型的,比如int,string等。
然后在子类初始化映射的时候,将代理实例化,也就是注册需要代理的函数的指针,以MyA子类的ID属性为例,它由setID(int id),和getID()两个方法操纵,所以在写数据库表A的属性ID与子类成员映射时将两个方法的指针注册:

new object[]{"Int32",new DelegateSetValue<int>(setID),new DelegateReadValue<int>(getID),0,"ID"}

好的,现在我们在父类里就可以对子类对象的成员变量赋值了,以查询数据库为例:

 //已经从DC获取了查询结果,rdr指向结果集第一行数据
 //m_dicProperty2DBFieldMa维护数据库到成员变量的映射列表
 protected void LoadEntityFromDataReader(MySqlDataReader rdr)
        {
            object[] mapRow;
            foreach(var item in m_dicProperty2DBFieldMap)
            {
                mapRow = item.Value;
                switch (mapRow[0].ToString())
                {
                    case "Int32": 
                        (mapRow[1] as DelegateSetValue<int>)(Convert.ToInt32(rdr[(int)mapRow[3]]));
                        break;
                    case "String": 
                        (mapRow[1] as DelegateSetValue<string>)(Convert.ToString(rdr[(int)mapRow[3]]));
                        break;
                    case "long":
                        (mapRow[1] as DelegateSetValue<long>)(Convert.ToInt64(rdr[(int)mapRow[3]]));
                        break;
                    case "enum":
                        (mapRow[1] as DelegateSetValue<int>)(Convert.ToInt32(rdr[(int)mapRow[3]]));
                        break;
                }
            }
        }

开始mapRow是Object数组,其元素都是Object对象,所以我们强行转换了mapRow[1]为DelegateSetValue类型,并将rdr读出来的ID值传给setID方法,修改子类对象ID的值。

至此,我们实现了C#中让父类调用子类函数的功能,那么在Java中呢?目前有两种思路,1.使用抽象类(或者接口更合理些)写代理,2.使用java的反射机制,获取子类的函数指针或者成员变量指针(感觉这个略强大。。。)
我们来研究一下第一种思路
定义一个接口,同样使用模板类:

public interface Interface4SetGet<T> {
    public abstract void SetValue(T data);
    public abstract T GetValue();
}

子类们去实现这个接口:

m_Property2DBFieldMap.put("m_iID",new Object[]{"Integer",new Interface4SetGet<Integer>{
public void SetValue(int id);
},0,"id"});

注意Java的Map和c#的Dictionary结构对应
然后在父类加载子类成员变量与数据库表的映射时,写类似于如下的代码:

//已从DC获取查询结果集,cur指向结果的第一行
protected void LoadEntityFromDataReader(Cursor cur)
    {
        Object[] mapRow;
        for(Map.Entry<String,Object[]> entry : m_Property2DBFieldMap.entrySet())
        {
            mapRow = entry.getValue();
            switch (mapRow[0].toString())
            {
                case "Integer":
                    ((Interface4SetGet<Integer>)mapRow[1]).SetValue(cur.getInt((int) mapRow[2]));
                    break;
                case "String":
                    ((Interface4SetGet<String>)mapRow[1]).SetValue(cur.getString((int) mapRow[2]));
                    break;
                case "long":
                    ((Interface4SetGet<Long>)mapRow[1]).SetValue(cur.getLong((int) mapRow[2]));
                    break;
                case "enum":
                    ((Interface4SetGet<Integer>)mapRow[1]).SetValue(cur.getInt((int) mapRow[2]));
                    break;
            }
        }
    }

由于子类映射初始化存的不再是函数指针,而是对象(接口的实现),所以调用这个对象的方法同样可以达到修改子类成员变量的目的。

我们再来讨论一下另外一种思路,使用反射
反射是什么,这里有个非常好的反射例子,如果是用反射得到method的指针,就和上面代理是一个道理了,主要来研究一下通过反射获得Field指针的方法。
在子类MyA中定义需要反射的变量,并且定义这些变量与相应Field的映射

 Map<String,Field> MemberName2Field = new HashMap<String,Field>();
 MemberName2Field.put(item,User.class.getDeclaredField(item));

同理,如果成员变量要与数据库交互,还需要将Field注册到与数据库交互的映射表里

m_Property2DBFieldMap.put("m_sItem",new Object[]{"String",MemberName2Field.get("m_sItem"),1,"item"});

映射表的定义和之前一样。
在AEntity就可以写复用代码了,还是以加载数据库的数据为例

//已从DC获取查询结果集,cur指向结果的第一行
protected void LoadEntityFromDataReader(Cursor cur)
    {
        Object[] mapRow;
        for(Map.Entry<String,Object[]> entry : m_Property2DBFieldMap.entrySet())
        {
            mapRow = entry.getValue();
            try {
                switch (mapRow[0].toString())
                {
                    case "Integer":((Field)mapRow[1]).set(this,cur.getInt(Integer.parseInt(mapRow[2].toString())));
                        break;
                    case "String": ((Field)mapRow[1]).set(this,cur.getString(Integer.parseInt(mapRow[2].toString())));
                        break;
                    case "long":((Field)mapRow[1]).set(this,cur.getLong(Integer.parseInt(mapRow[2].toString())));
                        break;
                }
            }catch (Exception ex)
            {
                Log.e(LOG_TAG,ex.toString());
            }
        }
    }

Field自带get,set方法,所以不用自己写代理来完成函数指针的工作啦~~

讨论暂时到此,分享请注明转载出处哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值