C#4.0 动态绑定(Dynamic Binding)

  .NET 4.0引入了一个新概念,叫动态绑定。所谓绑定,就是对类型、成员和操作的解析过程。动态绑定意味着与编译器无关,而与运行时有关。

一、    基本概念

C# 4.0之前的变量声明,在编译时已经决定其类型,虽然C# 3.0提供了var关键字来声明隐式类型,但用var声明的变量终究逃不过编译器的法眼。用dynamic声明变量的好处是,编译器在编译时并不知道它引用对象的确切类型,而要到运行时才知道。因此,我们可以通过动态绑定在运行时来操作一些类型的属性、方法等(如与IronPython,IronRuby,COM互操作),而这些在编译时编译器并不知情。

dynamic d = GetSomeObject();

d.Foo();

     此处代码在编译时,编译器并未将Foo()方法绑定到变量d, 而是延时到运行时才绑定。我们可以通过IL DASM查看,会发现,当有动态类型时,会交给DLR去处理。

    二、    静态绑定 (Static Binding)VS 动态绑定 (Dynamic Binding)

C# 4.0之前,每当声明一个变量时,都有一个具体的类型与之对应。因为编译器在编译时会进行类型检查,一旦出现类型方法匹配失败,语言错误等编译器就会报错。请看下面的两行代码:

SomeType st = ...

st.DoSomething();

当编译器编译这段代码时,最简单的情况是编译器绑定SomeType类型的DoSomething无参方法。如果失败,则查找SomeTypeDoSomething可选参数版本或SomeType基类方法。如果还失败,再查找SomeType的扩展方法版本。如果再失败,编译器就会报编译错误。我们可以看出,静态绑定是编译器基于可知类型之上的绑定。

现在,我们把st变为Object类型, 如下:

Object st = ...

st.DoSomething();

编译时,我们也会得到一个编译错误,因为Object类型并没有DoSomething()方法。我们再修改代码如下:

dynamic st = ...

st.DoSomething();

dynamic就是一个难以具体描述的对象,虽然你可以使用,但它却是在运行时基于它的运行时类型的绑定。因为它不是一个编译时类型,当编译器在编译时发现有动态类型,就把绑定交给运行时来做,即DLR

三、    自定义绑定(Custom Binding) VS 语言绑定(Language Binding)

自定义绑定发生在所有实现了 System.Dynamic.IDynamicMetaObjectProvider 接口的类型上。因为在 C# 4.0 的动态类型世界里,实现了 System.Dynamic.IDynamicMetaObjectProvider 接口的类型意味着对该类的实例的操作都会在运行时进行。如下面代码所示:

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Dynamic;
namespace  CustomBinding
{
    
public   class  SomeType : DynamicObject
    {
        
public   override   bool  TryInvokeMember(InvokeMemberBinder binder,  object [] args,  out   object  result)
        {
            Console.WriteLine(binder.Name 
+   "  method is calling. " );
            result 
=   null ;
            
return   true ;
        }
    }

    
public   class  Program
    {
        
static   void  Main()
        {
            dynamic d 
=   new  SomeType();
            d.DoOneThing();
            d.DoOtherThing();
        }
    }
}

由于DynamicObject类实现了IDynamicMetaObjectProvider接口,所以SomeType就是自定义绑定类型。从上面代码中我们可以看出,SomeType类并没有定义DoOneThing()方法与DoOtherThing()方法, 由于方法绑定是在运行时进行的,因此我们编译这段代码时就不会在编译时得到任何错误信息。

   语言绑定发生在没有实现IDynamicMetaObjectProvider接口的dynamic对象上。语言绑定是个非常有用的功能,下面的代码只是它的一个简单示例,我们可以看出,它与泛型算法有异曲同工之妙,如下面代码所示:

ExpandedBlockStart.gif 代码
using  System;
using  System.Dynamic;
namespace  LanguageBinding
{
    
public   class  Program
    {
        
static   void  Main()
        {
            
int  x  =   2 , y  =   6 ;
            
int  result  =  Compute(x, y);
            Console.WriteLine(result);
        }

        
static  dynamic Compute(dynamic x, dynamic y)
        {
            
return  (x  +  y)  /   2 ;
        }
    }
}

四、    RuntimeBinderException

对于动态绑定,在运行时绑定失败时,会抛出一个RuntimeBinderException异常。如下面代码所示会得到一个RuntimeBinderException

dynamic d = 5;

d.Hello();//此处抛出RuntimeBinderException异常

五、    动态转换(Dynamic Conversions)

动态(dynamic)类型与其它类型之间可以相互隐式转换。如:

int i = 5;

dynamic d = i;

int j = d; //这些赋值操作都不需要显示转换

long l = d; //运行时知道dint类型,int类型可以隐式转换成long类型

如果转换不成功,运行时分抛出一个RuntimeBinderException异常。如下所示:

int i = 5;

dynamic d = i;

short s = d; //此处抛出一个RuntimeBinderException异常,因为int不能隐式转换成short类型

六、    动态表达式中的静态类型

   一般说来,动态类型在动态绑定中使用,但有时候,动态类型却能在静态方法中使用,请看下面的示例:

 

ExpandedBlockStart.gif 代码
using  System;
public   class  Program
{
    
static   void  Test( int  i,  string  s) { Console.WriteLine( " int and string " ); }
    
static   void  Test( string  s,  int  i) { Console.WriteLine( " string and int " ); }
    
static   void  Main()
    {
        
int  i  =   10 ;
        dynamic d 
=   " hello " ;
        Test(i, d); 
// 调用此方法输出”int and string”
    }
}

虽然d是动态类型,但作为参数传递给Test方法后,还是能成功匹配Test(int i, string s)方法,且没有产生编译时异常,说明编译器对动态类型参数d作为静态类型对待的。

七、    不可调用函数

有些函数是不能通过动态绑定调用的,它们是:

1.      扩展方法(通过扩展方法语法调用)

2.      接口成员(特指显示实现的接口成员)

3.      受可访问性限制的方法

理解这些对于理解动态动态绑定非常有用。对于扩展方法,我们知道,是对于某个特定类型的,而且这个类型必须在编译时就能确定,由于动态绑定不能在编译时确定,所有不能动态调用扩展方法,如果执行这种代码,会产生一个 RuntimeBinderException 异常。如下所示:

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Dynamic;
namespace  DynamicExtensionMethod
{
    
public   class  Foo { }
    
public   static   class  TestExtensionMethod
    {
        
public   static   void  Test( this  Foo f)
        {
            Console.WriteLine(
" Foo class’s extension method. " );
        }
    }
    
class  Program
    {
        
static   void  Main()
        {
            Foo f 
=   new  Foo();
            f.Test();
// 此处扩展方法正常调用
            dynamic d  =   new  Foo();
            d.Test();
// 此处抛出一个RuntimeBinderException异常
        }
    }
}

对于显示实现的接口成员,由于实现接口的接口类型可能在另一个程序集的类中,虽然可以调用隐式实现的接口方法,但也不推荐这样做。调用显示实际的接口方法,同样会抛出一个RuntimeBinderException异常。如下所示:

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Dynamic;
namespace  DynamicInterface
{
    
interface  IFoo {  void  Test(); }
    
class  Foo : IFoo
    {
        
void  IFoo.Test() { Console.WriteLine( " Test " ); }
        
public   void  SayHello() { Console.WriteLine( " hello " ); }
    }
    
class  Program
    {
        
static   void  Main()
        {
            IFoo f 
=   new  Foo();
            dynamic d 
=  f;
            d.SayHello();
// 此处正常调用
            d.Test(); // 此处抛出RuntimeBinderException异常
        }
    }
}
 

 

受访问性限制的方法也是不能通过动态绑定调用的,不管是当前类或者当前类的任何基类,由类的封装性所决定,如下代码所示:

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Dynamic;
namespace  CallBaseMember
{
    
class  BaseClass
    {
        
private   void  Test() { Console.WriteLine( " Base Class Test " ); }
    }
    
class  SubClass : BaseClass
    {
        
private   void  Print() { Console.WriteLine( " Subclass’s print() " ); }
    }
    
class  Program
    {
        
static   void  Main()
        {
            SubClass subc 
=   new  SubClass();
            dynamic d 
=  subc;
            d.Test();
// 抛出一个RuntimeBinderException异常
            d.Print(); // 抛出RuntimeBinderException异常
        }
    }
}

九、    动态绑定的局限性

动态绑定能减少我们的类型转换次数,提升开发速度,但同时也有着它的局限性,主要有:

1. 有限的IDE支持

2.      通常情况下,意味更低的性能。特别是第一次调用方法的时候,因为DLR还没有缓存方法调用。

3.      更加复杂的开发

十、    总结

C# 4.0的动态绑定是基于DLR之上,它使我们在C#中实现动态语言编程,让我们的开发速度变得更快。

 

  参考文献:1、《C# 4.0 in nutshell》 4th Edtion,作者:Joseph Albahari, Ben Albahari;

       2、《Introducing .NET 4.0 With Visual Studio 2010》,作者:Alex Mackey。


 

 

 

 

 

 

转载于:https://www.cnblogs.com/myshell/archive/2010/03/15/1685853.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值