zoukankan      html  css  js  c++  java
  • 蛙蛙推荐:利用操作符重载提高程序的可读性

    摘要:以前做过一个简单的ORMapping的小组件,但c#代码转换成SQL代码的部分不是很直观,用到了很多丑陋的方法名来代替操作符,今天受脑袋指点,其实可以用操作符重载来实现c#直接写数据查询条件。

      c#可以重载很多操作符,对于SQL操作来说,主要是 ==,!=,&&,||(为了演示简单,暂不实现!,<,>,<=,>=等),其中前两个重载的话,还得必须重载true和false操作符,后两个不能直接重载,但可以重载&和|来代替。

    我们先定义一个表示数据库查询条件的类,如下

    代码
    public class DbCondition {
    public readonly string _fieldName = string.Empty;
    public string _sql = string.Empty;
    public DbCondition(string fieldName) {
    _fieldName
    = fieldName;
    }
    }

    有一些SQL的语法元素,如like,between,in在c#里没有对应的操作符,所以我们只能以方法的形式来实现它们了。

    代码
    private DbCondition() { }
    public DbCondition Like(string like) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} like '{1}'", _fieldName, like);
    return result;
    }
    public DbCondition Between(DateTime dt1, DateTime dt2) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} between '{1}' and '{2}'", _fieldName, dt1, dt2);
    return result;
    }



    public DbCondition In(IEnumerable<int> arr) {
    DbCondition result
    = new DbCondition();
    StringBuilder sb
    = new StringBuilder();
    foreach (int item in arr) {
    sb.AppendFormat(
    "{0},", item);
    }
    sb.Remove(sb.Length
    - 1, 1);
    result._sql
    = string.Format("{0} in ({1})", _fieldName, sb);
    return result;
    }
    public DbCondition In(IEnumerable<string> arr) {
    DbCondition result
    = new DbCondition();
    StringBuilder sb
    = new StringBuilder();
    foreach (string item in arr) {
    sb.AppendFormat(
    "'{0}',", item);
    }
    sb.Remove(sb.Length
    - 1, 1);
    result._sql
    = string.Format("{0} in ({1})", _fieldName, sb);
    return result;
    }

    现在轮到那些等于,不等于,与和或的实现了,其中逻辑运算符&和|的参数是能是两个同类型的参数,而比较操作符的第二个参数就可以是任意类型了,比如常见的int,bool,string,每个操作符重载的实现也比较简单,主要是操作_sql字段,拼写成SQL。

    代码
    public static DbCondition operator &(DbCondition x, DbCondition y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format(" {0} and {1} ", x._sql, y._sql);
    return result;
    }
    public static DbCondition operator |(DbCondition x, DbCondition y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format(" {0} or {1} ", x._sql, y._sql);
    return result;
    }
    public static DbCondition operator ==(DbCondition x, int y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} = {1}", x._fieldName, y);
    return result;
    }
    public static DbCondition operator !=(DbCondition x, int y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} <> {1}", x._fieldName, y);
    return result;
    }
    public static DbCondition operator ==(DbCondition x, string y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} = '{1}'", x._fieldName, y);
    return result;
    }
    public static DbCondition operator !=(DbCondition x, string y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} <> '{1}'", x._fieldName, y);
    return result;
    }
    public static DbCondition operator ==(DbCondition x, bool y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} = {1}", x._fieldName, y ? 1 : 0);
    return result;
    }
    public static DbCondition operator !=(DbCondition x, bool y) {
    DbCondition result
    = new DbCondition();
    result._sql
    = string.Format("{0} <> {1}", x._fieldName, y ? 1 : 0);
    return result;
    }

    因为我们重载了比较操作符和逻辑操作符,而一个bool表达式有短路的问题,比如一个或关系连接的两个bool表达式,如果左边的表达式返回false,右边的表达式就不执行了,所以我们还得重载true和false两个操作符,让他都返回false,如下

    public static bool operator false(DbCondition x) { return false; }
    public static bool operator true(DbCondition x) { return false; }

    OK,剩下的都是一些常规的重载了,都是统一的套路

    代码
    public override bool Equals(object obj) {
    DbCondition cond
    = obj as DbCondition;
    if (cond == null) return false;
    if (object.ReferenceEquals(this, cond) == true) return true;
    if (cond._fieldName != this._fieldName) return false;
    if (cond._sql != this._sql) return false;
    return true;
    }
    public override int GetHashCode() {
    int hashcode = 0;
    if (!string.IsNullOrEmpty(_fieldName)) hashcode ^= _fieldName.GetHashCode();
    if (!string.IsNullOrEmpty(_sql)) hashcode ^= _sql.GetHashCode() << 1;
    return hashcode;
    }

    public override string ToString() {
    return _sql;
    }

    下面开始准备测试了,先准备一个实体类,该实体类包含一些静态的条件,分别对应数据库表的列,真实情况下,这些实体类是由代码生成器生成的。

    代码
    public class User {

    public static DbCondition CdtUserId { get { return new DbCondition("UserId"); } }
    public static DbCondition CdtName { get { return new DbCondition("Name"); } }
    public static DbCondition CdtNickName { get { return new DbCondition("NickName"); } }
    public static DbCondition CdtSex { get { return new DbCondition("Sex"); } }
    public static DbCondition CdtRegistTime { get { return new DbCondition("RegistTime"); } }
    }

    写一些测试代码,看下效果

    代码
    Console.WriteLine(User.CdtName == "蛙蛙");
    Console.WriteLine(User.CdtName
    == "蛙蛙" || User.CdtNickName == "蛙蛙");
    Console.WriteLine(User.CdtName
    == "蛙蛙" && User.CdtSex == true);
    Console.WriteLine(User.CdtName.Like(
    "蛙蛙%")
    && User.CdtRegistTime.Between(DateTime.Now.AddMonths(-1), DateTime.Now));
    Console.WriteLine(User.CdtNickName
    != "蛙蛙"
    && User.CdtUserId.In(Enumerable.Range(100,10)));

    测试输出如下

    代码
    Name = '蛙蛙'
    Name
    = '蛙蛙' or NickName = '蛙蛙'
    Name
    = '蛙蛙' and Sex = 1
    Name like
    '蛙蛙%' and RegistTime between '2010-7-9 22:27:11' and '2010-8-9 22:2
    7:11'
    NickName <> '蛙蛙' and UserId in (100,101,102,103,104,105,106,107,108,109)

    这样写出来的对象查询语句和LINQ格式差不多了,很直观,而且生成的SQL也可以自由控制。

    相关链接

    Operator Overloading Tutorial

    蛙蛙推荐:简化基于数据库的DotNet应用程序开发

  • 相关阅读:
    Azure School女神相邀,把每分钟都过的更充实
    Java、Node.js、PHP还是.Net? 无论你选谁,我都能教你一招!
    一样的Java,不一样的HDInsight大数据开发体验
    第五代微软小冰 | 你有一个来自人工智能的电话待接听
    2017“编程之美”终章:AI之战勇者为王
    大数据freestyle: 共享单车轨迹数据助力城市合理规划自行车道
    语音识别技术里程碑:错误率降至5.1%,超过专业速记员
    wait和waitpid
    Linux网络编程wait()和waitpid()的讲解
    如何测试Linux 中的wait函数能不能等待子进程的子进程?
  • 原文地址:https://www.cnblogs.com/onlytiancai/p/Operator_Overloading.html
Copyright © 2011-2022 走看看