技术开发 频道

.NET入门:用dynamic增强C#泛型表达力

    【IT168 技术文档】C++的泛型是基于模板(template)技术的。模板本身并不作为独立的编译单元,而是在编译时绑定实际参数类型进行模板实例化,类似与C语言的宏展开,在运行时并不存在独立的模板类型。模板对泛型参数的约束是基于操作的语法特征的,属于一种静态的duck typing机制,十分灵活。

  下面的代码定义了一个泛型Add函数,它对泛型参数T的要求只是支持+运算,并不要求T是某个类的子类,或是实现了某个接口。int, double, std::string等支持+运算符的类型都可以成功匹配T。

//C++

template
<typename T>
T Add(
const T& t1, const T& t2) {
    return t1
+ t2;
}

int main() {
    
int i = Add(1, 2);
    
double d = Add(1.1, 2.2);
    std::
string s = Add(std::string("abc"), std::string("def"));

    std::cout
<< i << " " << d << " " << s << std::endl;

    return
0;
}

 

  输出:

>>3 3.3 abcdef

 

  而类似的代码在C#中却无法编译通过:

1

  这是由于C#采用基于reification的泛型机制,泛型类会单独编译,并且在运行时存在;因此,C#对于泛型参数的要求更加严格,只能通过where关键字表达基于继承关系的约束,无法通过duck typing的方式表达类型约束。与模板相比,这种机制的好处在于可以更好的支持反射和元编程,但其缺点是泛型的表达能力不如模板。幸好C#4.0中引入了动态类型机制,我们可以通过动态类型来实现基于duck typing的泛型参数约束。

//C#

static class Calculator {
    
public static T Add<T>(T t1, T t2) {
        dynamic d1
= t1;
        dynamic d2
= t2;

        return (T)(d1
+ d2);
    }
}

public static void Main(string[] args){
    
int i = Calculator.Add(1, 2);
    
double d = Calculator.Add(1.1, 2.2);
    
string s = Calculator.Add("abc", "def");

    Console.WriteLine(i
+ " " + d + " " + s);

}

 

  输出:

>>3 3.3 abcdef

 

  除了运算符重载,对于普通的方法调用也是适用的。这种方法是一种动态duck typing的泛型参数约束机制,依赖于运行时的方法查找,与模板编译时的检查不同,它需要使用者保证传入的对象符合相应要求。

0
相关文章