深入理解C#(第3版)-- 【C#4】第14章 静态语言中的动态绑定(学习笔记)

14.1   何谓、何时、为何、如何

14.1.1   何谓动态类型

编译器知道代码中表达式的类型,知道任何类型中可用的成员。它应用了相当复杂的规则来决定哪个成员应该在何时使用。这包括了重载决策;在(动态类型出现)之前的唯一途径是根据对象在执行时的类型,来选择虚方法的实现。决定使用哪个成员的过程称为绑定(binding),对于静态类型的语言来说,绑定发生在编译时。

而在动态类型的语言中,所有的绑定都发生在执行时。编译器或解析器可以检查语法是否正确,但却无法检查所调用的方法或所访问的属性是否真的存在。

14.1.2   动态类型什么时候有用,为什么

首先,如果你知道要调用的成员名称、要传入的参数以及要调用的对象.

动态类型的第二个重要的特性是,对象可以通过分析提供给它的名称和参数来响应某个调用。

14.1.3   C# 4如何提供动态类型

C# 4 引入了一个新的类型,称为dynamic。

事实上,dynamic并不代表一个特定的CLR 类型。它实际上只是包含System.Dynamic.DynamicAttribute特性的System.Object 。

14.2   关于动态的快速指南

 几乎所有CLR 类型都可以隐式转换为dynamic。
 所有dynamic类型的表达式都可以隐式转换为CLR 类型。
 使用dynamic类型值的表达式通常会动态地求值。
 动态求值表达式的静态类型通常被视为dynamic。

14.3   动态类型示例

Massive(https://github.com/robconery/massive)

Dapper(http://code.google.com/p/dapper-dot-net/)

Json.NET (http://json.codeplex.com)

14.3.1   COM和Office

14.3.2   动态语言

1. 为什么要在C#中使用IronPython

2. 入门:内嵌Hello, World 

3. 用ScriptScope存储和获取信息

4. 综合应用

关于依赖注入和控制反转,可以参考Martin Fowler著名的文章 http://www.martinfowler.com/articles/injection.html 

14.3.3   纯托管代码中的动态类型

1. 执行时类型推断

2. 弥补泛型操作符的不足

Summing a sequence dynamically

3. 鸭子类型

Gotchas in dynamic typing

4. 多重分发

14.4   幕后原理

http://blogs.msdn.com/b/samng/

http://mng.bz/0M6A

14.4.1   DLR简介

Dynamic Language Runtime 

http://mng.bz/0M6A

DLR 它真的仅仅只是一个库。尽管它同样以运行时为名,却与CLR (Common Language Runtime ,公共语言运行时)不在同一个级别——它不涉及JIT编译、本地API封送(marshal)、垃圾回收等内容。

http://dlr.codeplex.com

http://mono-project.com

14.4.2   DLR核心概念

1. 调用点

2. 接收器和绑定器

3. 规则和缓存

14.4.3   C#编译器如何处理动态

1. 如果使用了动态,那么它就是动态的

代码清单14-16  方法重载和动态值

static void Execute(string x)
{
    Console.WriteLine("String overload");
}

static void Execute(dynamic x)
{
    Console.WriteLine("Dynamic overload");
}

...
dynamic text = "text";
Execute(text); //打印"String Overload" 
dynamic number = 10;
Execute(number); //打印"Dynamic Overload"  

2. 它是动态的……除非例外情况 

3. CLR类型与动态类型之间的转换

4. 动态表达式并不总是动态地求值

5. 动态求值的表达式并不总是动态类型

6. 创建调用点和绑定器

14.4.4   更加智能的C#编译器

1. 在执行时保存编译器行为

代码清单14-18  单个类型中的动态重载决策

static void Execute(dynamic x, string y)
{
    Console.WriteLine("dynamic, string");
}
static void Execute(dynamic x, object y)
{
    Console.WriteLine("dynamic, object");
}

...
object text = "text";
dynamic d = 10;
Execute(d, text); //打印"dynamic, object"

代码清单14-19  类层次结构中的动态重载决策

class Base
{
    public void Execute(object x)
    {
        Console.WriteLine("object");
    }
}

class Derived : Base
{
    public void Execute(string x)
    {
        Console.WriteLine("string");
    }
}

...
Base receiver = new Derived();
dynamic d = "text";
receiver.Execute(d); //打印"object"

2. 动态代码的编译时错误

14.4.5   动态代码的约束

1. 不能动态处理扩展方法

这不仅意味着不能调用动态值的扩展方法,还意味着不能将动态值作为参数传入扩展方法。编译器推荐了两种变通方案。如果你知道使用哪个重载,可以在方法内将动态值转换为正确的类型。或者,假设你知道扩展方法所在的静态类型,可以像调用普通的静态方法那样进行调用。

2. 委托与动态类型之间转换的限制

3. 构造函数和静态方法

4. 类型声明和泛型类型参数

你不能声明一个基类为dynamic的类型。你同样不能将dynamic用于类型参数的约束,或作为类型所实现的接口的一部分。你可以将其用于基类的类型实参,或在声明变量时将其用于接口。

例如,下面的声明是无效的:
 class BaseTypeOfDynamic:dynamic
 class DynamicTypeConstraint<T>where T:dynamic
 class DynamicTypeConstraint<T>where T:List<dynamic>
 class DynamicInterface:IEnumerable<dynamic>
而以下声明则有效:
 class GenericDynamicBaseClass:List<dynamic>
 IEnumerable<dynamic> variable;

14.5   实现动态行为

14.5.1  使用ExpandoObject 

1. 设置或获取单独的属性

2. 创建DOM树

14.5.2  使用DynamicObject

1. 准备工作

2. DynamicObject 支持的简单成员

3. 覆盖TryXXX 方法

4. 覆盖GetDynamicMemberNames 

14.5.3  实现IDynamicMetaObjectProvider

代码清单14-35  不含元对象代码的Rumplelstiltskin 类型

public sealed class Rumpelstiltskin : IDynamicMetaObjectProvider
{
    private readonly string name;
    
    public Rumpelstiltskin(string name)
    {
        this.name = name;
    }
    
    public DynamicMetaObject GetMetaObject(Expression expression)
    {
        return new MetaRumpelstiltskin(expression, this);
    }
    
    private object RespondToWrongGuess(string guess)
    {
        Console.WriteLine("No, I'm not {0}! (I'm {1}.)",
                          guess, name);
        return false;
    }
    
    private object RespondToRightGuess()
    {
        Console.WriteLine("Curses! Foiled again!");
        return true;
    }
}

这两个方法为什么返回object ?

你可能会奇怪,为什么这两个方法返回 object 而不是bool呢?实际上,我一开始实现的是void方法,但不幸的是动态方法调用必须有返回值,并且就我的经验来看,绑定器总是希望能返回object。(可以检查ReturnType 属性。)在执行时调用void方法将抛出异常,bool方法也是如此;我们需要自行装箱,使类型能够匹配。我们可以将装箱构建在表达式中,但修改方法的返回类型要简单得多。

posted @ 2019-10-18 18:28  FH1004322  阅读(240)  评论(0)    收藏  举报