泛型之T运算 二

 

注意:未经许可,本系列禁止转载。

本文所介绍的技巧,是我在研究泛型开发不久就发现并成功运用的技巧。这个技巧是突破.NET泛型限制,达到“看上去很美”境界的法宝。当然本方法也存在重大缺陷,后面我会逐一介绍。本文同时使用VB和C#语法,以下是泛型方面VB和C#的语法小小对照:

VBC#
Identifier(Of T)Identifier<T>
Identifier(Of T As C)Identifier<T> where T : C

上次我们介绍了约束模型的缺陷和使用外部辅助类代替约束的手法。现在我们继续研究该手法。如上次所说,基本数值类型Integer, Long和Double等并没有实现什么公共的接口以实现普通加减乘除等运算;在泛型类和方法中,类型参数的变量之间也不允许使用+、-等运算符,所以我们别无选择,只能使用外部辅助类。以下就是我编写的辅助类Calculator。他是这样工作的:

Function  Add(Of T)(a  As  T, b  As  T) As  T
     Return  Calculator(Of T).Default.Add(a, b)
End Function

' 在另一个方法中
Dim  c  As   Integer   =  Add( 1 ,  2 )
Dim  d  As   Double   =  Add( 1.5 ,  888.0 )

我们看到,Calculator(Of T).Default能够在T确定为Integer或Double的时候分别给出正确的整数相加及浮点数相加运算。.NET泛型本身是缺乏这种编译时类型选择功能的,也就是说,无法根据类型参数T的不同自动选择不同的代码执行。那么是不是只能用运行时If语句加类型判断了呢?如果是那样,运行时效率必然难以保证,同时也失去了使用泛型的意义。所以我们得求助一个工具——类型字典。

“类型字典”这个词是我根据其特性杜撰的,它其实利用到.NET泛型很重要的一个特性:泛型类的静态字段。如果有一个泛型类型A(Of T),那么它的每一个 封闭构造类型(Closed Constructed Type)独享一组静态字段的取值。也就是说,假设X是A(Of T)的静态字段(VB中的Shared或C#中的static),那么A(Of Integer).X与A(Of String).X是互相独立的两个字段,改变一个的值不会影响到另一个。这样就给我们提供了一个极为便利的条件,我们可以利用泛型类型的静态字段,为类型参数的每一组封闭取值(封闭的意思是所有取值为非泛型类型或泛型封闭构造类型)保存一个字段的值。简单地说,这就相当于以类型作为关键字,建立了一个字典,其值则是一个对象。以下为C#语法的一个小小的例子:
class  TypeDict < T >
{
     public   static   int  Value;
}

// 在代码中,我们可以为每个封闭类型保存一个int
// 这相当于一个 类型到int 的字典
TypeDict < int > .Value  =   1 ;
TypeDict < string > .Value  =   2 ;
TypeDict < List < int >> .Value  =   3 ;
TypeDict < List < double >> .Value  =   4 ;

从类型查询到其相应的值,只有访问一个静态字段的代价,可谓极低。下面我们就利用这个优势,实现根据类型参数的不同,自动给出适合该类型的算术运算功能。我们统称这种类型参数取不同取值,类的功能就自动变化的功能为type traits,意为“类型特征”。.NET泛型不支持特化,因此不能做编译期选择的type trait,只能将这个过程推迟到运行期。为了减少运行期的消耗,我们采用首次运行进行类型判断,然后用类型字典保存判断结果的手法,我称之为 首次缓存的拟type traits。下面用四则运算为例介绍做法:为了简化,我们现在只考虑四则运算功能中的加法。以下是在同类型上实现加法的接口:
Public   Interface  ICalculator(Of T)
     Function  Add(a  As  T, b  As  T) As  T
    
End Interface

我们要给ICalculator接口提供一个具有type trait功能的默认实现,首先声明一个类型:
Public   MustInherit   Class  Calculator(Of T)
     Implements  ICalculator(Of T)

     Public   MustOverride   Function  Add(a  As  T, b  As  T)  As  T _
         Implements  ICalculator(Of T).Add
    
End Class

注意这是一个抽象类,我们还没有写完他,但当前要做的事情是,为Integer,Double和T(代表未知类型)编写三个特化子类,分别封装Integer, Double和未知类型的加法。
Class  IntegerCalc
     Inherits  Calculator(Of  Integer )

     Public   Overrides   Function  Add( ByVal  a  As   Integer ,  ByVal  b  As   Integer )  As   Integer
         Return  a  +  b
     End Function
End Class

Class  DoubleCalc
     Inherits  Calculator(Of  Double )

     Public   Overrides   Function  Add( ByVal  a  As   Double ,  ByVal  b  As   Double )  As   Double
         Return  a  +  b
     End Function
End Class

Class  ObjectCalc(Of T)
     Inherits  Calculator(Of T)

     Public   Overrides   Function  Add( ByVal  a  As  T,  ByVal  b  As  T)  As  T
         ' 后期绑定的加法运算
         ' VB的后期绑定支持运算符重载
         Return   DirectCast ( CObj (a)  +   CObj (b), T)
     End Function
End Class

给特殊类型的子类编写加法出奇的容易,因为类型变成了已知类型,因此运算符也就能够允许使用。注意我我们给ObjectCalc编写的加法过程使用了后期绑定加法,因此可以支持任何重载+号的类型,但是他的速度要比IntegerCalc和DoubleCalc中的做法慢很多倍。因此在实际代码中,我为.NET中具有算术加法概念的许多类型,如整型,浮点型,Decimal和Nullable类型都做了特殊子类。而ObjectCalc则为那些没有考虑到的类型提供低性能,但仍起作用的最低保证。 下面我们用If语句模拟编译期的类型选择机制,并保存在Calculator(Of T)的静态字段里,做成类型字典。修改Calculator如下
Public   MustInherit   Class  Calculator(Of T)
     Implements  ICalculator(Of T)

     Public   MustOverride   Function  Add( ByVal  a  As  T,  ByVal  b  As  T)  As  T _
         Implements  ICalculator(Of T).Add

     ' 保存类型T的字典值
     Private   Shared  defaultCalc  As  Calculator(Of T)

     Public   Shared   ReadOnly   Property  [ Default ]()  As  Calculator(Of T)
         Get
             If  defaultCalc  Is   Nothing   Then  CreateCalculator()
             Return  defaultCalc
         End   Get
     End Property

     Private   Shared   Sub  CreateCalculator()
         Dim  calcObj  As   Object
         ' 根据类型进行特殊子类的指派
         If   GetType (T).Equals( GetType ( Integer ))  Then
            calcObj  =   New  IntegerCalc
         ElseIf   GetType (T).Equals( GetType ( Double ))  Then
            calcObj  =   New  DoubleCalc
         Else
             ' 未知类型
            calcObj  =   New  ObjectCalc(Of T)
         End   If
         ' 强行让编译器认为我们选择类型就是T
        defaultCalc  =   DirectCast (calcObj, Calculator(Of T))
     End Sub
End Class

下面我们来看看这是如何工作的,假设我们要进行Integer的计算,那么首先写下Calculator(Of Integer).Default,这是第一次运行,所以它调用CreateCalculator方法。这个方法利用GetType运算符(C#的typeof)判断T是否是我们写过的特殊子类实现的类型,并且给静态字段defaultCalc分配一个特殊子类。这时候Default的值其实就是IntegerCalc的实例。当你第 次运行Calculator(Of Integer).Default的时候,缓存在Calculator(Of Integer)类型字典中的IntegerCalc实例会被直接调用,不再会进行第一次的类型判断。也就是说从第 次起,我们Integer运算的加法就可以好像编译期选择的那样,全速执行了。

类型字典+首次缓存的拟type traits可以漂亮地做出自动根据类型进行选择的功能,但是他也不是没有缺陷的。首先是这种类型选择不能轻易的扩展。假如将Calculator的代码做到类库里,那么从外部想给他加入一个特殊类型子类是无法轻易做到的,因为基于If的判断语句不易扩展,改进的做法可能通过一个(普通)字典作登记。或者结合面向对象,不采用这种方法扩充,而由用户自行实现ICalculator,然后取代默认Calculator(这正是.NET Framework和VBF采用的做法)。第 个缺陷是保存在静态字段中的对象不会被清理,这样就会越用越多,占据内存。好在这种用于计算或比较类的对象都很小。

完整代码:

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

using XZL.Generic.Error;

 

namespace XZL

{

    namespace Generic.MyMath

    {

        public interface ICalculator<T>

        {

            T Add(T a, T b);

            T Subtraction(T a, T b);

            T Multiply(T a, T b);

            T Divide(T a, T b);

            bool More(T a, T b);

            bool Less(T a, T b);

            bool ValueEqual(T a, T b);

 

        }

 

        public abstract class Calculator<T> : ICalculator<T> 

        {

            private static Calculator<T> m_default;

 

            public static Calculator<T> Default

            {

                get

                {

                    if (m_default == null)

                    {

                        CreateCalculator();

                    }

                    return m_default;

                }

            }

 

            public abstract T Add(T a, T b);

            public abstract T Subtraction(T a, T b);

            public abstract T Multiply(T a, T b);

            public abstract T Divide(T a, T b);

            public abstract bool More(T a, T b);

            public abstract bool Less(T a, T b);

            public abstract bool ValueEqual(T a, T b);

 

            public abstract T CreateObject();

            public static void  CreateCalculator()

            {

                object calObj;

                if (typeof(T) == typeof(int))

                {

                    calObj = new IntegerCalc();

                }

                else if (typeof(T) == typeof(double))

                {

                    calObj = new DoubleCalc();

                }

                else if (typeof(T) == typeof(Complex))

                {

                    calObj = new ComplexCalc();

                }

                else

                {

                    calObj = new ObjCalc<T, U>();

                }

                m_default = (Calculator<T,U>)calObj;

            }

        }

 

        public class IntegerCalc:Calculator<int>

        {

            public override int Add(int a, int b)

            {

                return a + b;

            }

            public override int Divide(int a, int b)

            {

                if (b == 0)

                {

                    throw new Exception("除数b不能为0");

 

                }

                else 

                {

                    return a / b;

 

                }

            }

            public override int Multiply(int a, int b)

            {

                return a * b;

            }

            public override int Subtraction(int a, int b)

            {

                return a - b;

            }

 

            public override bool More(int a, int b)

            {

                return a > b;

            }

            public override bool Less(int a, int b)

            {

                return a < b;

            }

            public override bool ValueEqual(int a, int b)

            {

                return a == b;

            }

 

         }

 

        public class DoubleCalc:Calculator<double>

        {

            public override double Add(double a, double b)

            {

                return a + b;

            }

            public override double Divide(double a, double b)

            {

                if (b == 0)

                {

                    throw new Exception("除数b不能为0");

 

                }

                else

                {

                    return a / b;

 

                }

            }

 

            public override double Subtraction(double a, double b)

            {

                return a - b;

            }

            public override bool More(double a, double b)

            {

                return a > b;

            }

            public override double Multiply(double a, double b)

            {

                return a * b;

            }

            public override bool Less(double a, double b)

            {

                return a < b;

            }

            public override bool ValueEqual(double a, double b)

            {

                return a == b;

            }

 

 

        }

 

        public class ComplexCalc : Calculator<Complex>

        {

            public override Complex Add(Complex a, Complex b)

            {

                return a + b;

            }

            public override Complex Divide(Complex a, Complex b)

            {

                if (b == new Complex(0, 0))

                {

                    throw new Exception("除数b不能为0");

 

                }

                else

                {

                    return a / b;

 

                }

            }

            public override Complex Multiply(Complex a, Complex b)

            {

                return a * b;

            }

            public override Complex Subtraction(Complex a, Complex b)

            {

                return a - b;

            }

            public override bool More(Complex a, Complex b)

            {

                return a > b;

            }

            public override bool Less(Complex a, Complex b)

            {

                return a < b;

            }

            public override bool ValueEqual(Complex a, Complex b)

            {

                return a == b;

            }

 

        }

 

        public class ObjCalc<T>:Calculator<T>

{       

       private IntegerCalc m_intc;

            private Assembly m_ass;

            private object m_obj;

            private Type m_type;

            private object[] m_parameters;

            public ObjCalc()

            {

                m_intc = new IntegerCalc();

                m_ass = Assembly.GetAssembly(m_intc.GetType());

                m_obj = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(m_ass.FullName, "XZL.Generic.MyMath.TOperater");

                m_type = m_obj.GetType();

                m_parameters = new object[2];

            }

            public override T Add(T a, T b)

            {

 

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    T ob = (T)m_type.InvokeMember("Add", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.Add", e.Message);

                //    return default(T);

                //}

                throw new Exception("The method or operation is not implemented.");

            }

 

            public override T Divide(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    T ob = (T)m_type.InvokeMember("Divide", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.Divide", e.Message);

                //    return default(T);

                //}

                throw new Exception("The method or operation is not implemented.");

            }

            public override T Subtraction(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    T ob = (T)m_type.InvokeMember("Subtraction", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.Subtraction", e.Message);

                //    return default(T);

                //}

                throw new Exception("The method or operation is not implemented.");

            }

            public override T Multiply(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    T ob = (T)m_type.InvokeMember("Multiply", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.Add", e.Message);

                //    return default(T);

                //}

                throw new Exception("The method or operation is not implemented.");

            }

            public override bool More(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    bool ob = (bool)m_type.InvokeMember("More", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.More", e.Message);

                //    return false;

                //}

                throw new Exception("The method or operation is not implemented.");

 

            }

            public override bool ValueEqual(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    bool ob = (bool)m_type.InvokeMember("ValueEqual", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.ValueEqual", e.Message);

                //    return false;

                //}

                throw new Exception("The method or operation is not implemented.");

 

            }

            public override bool Less(T a, T b)

            {

                //try

                //{

                //    m_parameters[0] = (object)a;

                //    m_parameters[1] = (object)b;

                //    bool ob = (bool)m_type.InvokeMember("Less", BindingFlags.InvokeMethod, null, m_obj, m_parameters);

                //    return ob; ;

                //}

                //catch (System.Exception e)

                //{

                //    EventLogAndDebug.EventLogWriteLine("XZL.MatlabLib.ObjCalc<T>.Less", e.Message);

                //    return false;

                //}

                throw new Exception("The method or operation is not implemented.");

 

            }

 

        }

    }

 

}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值