泛型的运算符重载

分享.NET by 达达 at 2006-07-13

前些天,跟园里的henry谈到了泛型的运算符重载,henry目前做的SQL Artisan对SQL语句进行了封装,避免了SQL语句无法在编译时期进行错误判断的缺陷,又保持了SQL语句的灵活性,但是目前SQL Artisan不支持泛型,hanry还没做过泛型的运算符重载,他觉得好像不能实现。今天早上起床时我试验了下,发现泛型的运算符重载是可以实现的,用泛型可以省掉很多的代码。

假设我们不使用泛型,要对SQL语句进行封装,让我们的C#语句最后可以生成SQL语句,并且要保持类型安全性,例如代码 :

TableA.CreateTime == DateTime.Now & TableB.Name == "Peter"

并且TableA.CreateTime == DateTime.Now换成TableA.CreateTime == "2007-7-13" 时编译不能通过,那么我们是不是需要为每种SQL数据类新封装一个类,用来做SQL语句生成之用,除了为每种类型封装一个类外我想不到别的更好的办法,但是如果泛型支持运算符重载,那么我们的代码就可以节省很多了,可能光说大家不太理解。

现在我们就来看看泛型类的运算符是怎么重载的,怎样实际减少我们的代码量的。

假设这里我们要对SQL的条件语句进行封装并让其支持泛型,SQL条件语句类我命名为SqlFilter,SQL语句类我命名为SqlExpression

我的简单封装结果如下(这里没有用到数据库参数,但是实际应用中应该考虑使用数据库参数而不是单纯字符串连接):

   public class SqlFilter<T>
   {
        private string _name = null;

        public string Name
        {
            get { return _name; }
        }

        public SqlFilter(string name)
        {
            _name = name;
        }

        public static SqlExpression operator ==(SqlFilter<T> t, T value)
        {
            SqlExpression _exp = new SqlExpression(
              string.Format("{0} = '{1}'", t.Name, value.ToString())
            );
            return _exp;
        }

        public static SqlExpression operator !=(SqlFilter<T> t, T value)
        {
            SqlExpression _exp = new SqlExpression(
              string.Format("{0} <> '{1}'", t.Name, value.ToString())
            );
            return _exp;
        }

        public override int  GetHashCode()
        {
          return base.GetHashCode();
        }
    }

    public class SqlExpression
    {
        public string _expression;
        public IDataParameter[] _parameters;

        public SqlExpression(string expression, params IDataParameter[] patameters)
        {
            _expression = expression;
            _parameters = patameters;
        }

        public override string ToString()
        {
            return _expression;
        }

        public IDataParameter[] Parameters
        {
            get { return _parameters; }
        }

        public IDataParameter this[int index]
        {
            get { return this._parameters[index]; }
        }

        public static string operator &(SqlExpression exp1, SqlExpression exp2)
        {
            return string.Format("{0} And {1}", exp1, exp2);
        }

        public static string operator |(SqlExpression exp1, SqlExpression exp2)
        {
            return string.Format("{0} Or {1}", exp1, exp2);
        }
    }

当T为DateTime类型时==运算符就接收DateTime类型的值,T为int类型时==运算符就接收int型的值,如果值类型和T的类型不匹配编译不通过。

我们可以写个简单的测试代码:

class Program
{
    static void Main(string[] args)
    {
        SqlFilter _mc1 = new SqlFilter("TimeValue");
        SqlFilter _mc2 = new SqlFilter("IntValue");

        Console.WriteLine(_mc1 == DateTime.Now & _mc2 == 100);
        Console.WriteLine(_mc1 == DateTime.Now | _mc2 != 100);

        Console.Read();
    }
}

运行下看看结果,如预计效果一样。

我一开始也怀疑会不会失去了类型判断,我就把_mc1 == DateTime.Now 改成了 _mc1 == "123",果然编译不通过,呵呵,看来泛型的运算符重载还是有效的,henry的SQL Artisan不知道会不会开始考虑支持翻型,我的简陋持久层是肯定要用这样形式封装下SQL条件的。

全部代码(推荐使用Snippet Compiler直接运行,不用再新建项目):

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace 泛型运算符重载
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlFilter<DateTime> _mc1 = new SqlFilter<DateTime>("TimeValue");
            SqlFilter<int> _mc2 = new SqlFilter<int>("IntValue");

            Console.WriteLine(_mc1 == DateTime.Now & _mc2 == 100);
            Console.WriteLine(_mc1 == DateTime.Now | _mc2 != 100);
            
            Console.Read();
        }
    }

    //SQL
    public class SqlFilter<T>
    {
        private string _name = null;

        public string Name
        {
            get { return _name; }
        }

        public SqlFilter(string name)
        {
            _name = name;
        }

        public static SqlExpression operator ==(SqlFilter<T> t, T value)
        {
            SqlExpression _exp = new SqlExpression(
                string.Format("{0} = '{1}'", t.Name, value.ToString())
            );
            return _exp;
        }

        public static SqlExpression operator !=(SqlFilter<T> t, T value)
        {
            SqlExpression _exp = new SqlExpression(
                string.Format("{0} <> '{1}'", t.Name, value.ToString())
            );
            return _exp;
        }

        public override int  GetHashCode()
        {
          return base.GetHashCode();
        }
    }

    public class SqlExpression
    {
        public string _expression;
        public IDataParameter[] _parameters;

        public SqlExpression(string expression, params IDataParameter[] patameters)
        {
            _expression = expression;
            _parameters = patameters;
        }

        public override string ToString()
        {
            return _expression;
        }

        public IDataParameter[] Parameters
        {
            get { return _parameters; }
        }

        public IDataParameter this[int index]
        {
            get { return this._parameters[index]; }
        }

        public static string operator &(SqlExpression exp1, SqlExpression exp2)
        {
            return string.Format("{0} And {1}", exp1, exp2);
        }

        public static string operator |(SqlExpression exp1, SqlExpression exp2)
        {
            return string.Format("{0} Or {1}", exp1, exp2);
        }
    }
}