C# - 动态类型 Dynamic Types

C# 4.0 (.NET 4.5) 带来了一种新类型 dynamic,可避免编译时类型检查。 动态类型在编译时逃避类型检查; 相反,它在运行时解析类型。

动态类型变量是使用 dynamic 关键字定义的。

dynamic MyDynamicVar = 1;

在大多数情况下,编译器将动态类型编译为object类型。 但是,动态类型变量的实际类型将在运行时解析。

dynamic MyDynamicVar = 1;

Console.WriteLine(MyDynamicVar.GetType()); //System.Int32

动态类型在运行时根据分配的值更改类型。 以下示例显示动态变量如何根据分配的值更改类型。

static void Main(string[] args)
{
    dynamic MyDynamicVar = 100;
    Console.WriteLine("Value: {0}, Type: {1}", MyDynamicVar, MyDynamicVar.GetType());

    MyDynamicVar = "Hello World!!";
    Console.WriteLine("Value: {0}, Type: {1}", MyDynamicVar, MyDynamicVar.GetType());

    MyDynamicVar = true;
    Console.WriteLine("Value: {0}, Type: {1}", MyDynamicVar, MyDynamicVar.GetType());

    MyDynamicVar = DateTime.Now;
    Console.WriteLine("Value: {0}, Type: {1}", MyDynamicVar, MyDynamicVar.GetType());
}
// 输出
// Value: 100, Type: System.Int32
// Value: Hello World!!, Type: System.String
// Value: True, Type: System.Boolean
// Value: 01-01-2014, Type: System.DateTime

将动态类型隐式转化成其它类型

dynamic d1 = 100;
int i = d1;
		
//string i2 = d1; 运行时出错

d1 = "Hello";
string greet = d1;
		
d1 = DateTime.Now;
DateTime dt = d1;

调用方法

如果将类对象分配给动态类型,则编译器将不会检查包含自定义类对象的动态类型的正确方法和属性名称。 考虑以下示例。

class Program
{
    static void Main(string[] args)
    {
        dynamic stud = new Student();

        stud.DisplayStudentInfo(1, "Bill");// 运行时错误, 非编译时出错
        stud.DisplayStudentInfo("1");//运行时错误, 非编译时出错
        stud.FakeMethod();// 运行时错误, 非编译时出错
    }
}

public class Student
{
    public void DisplayStudentInfo(int id)
    {
    }
}

在上面的示例中,C# 编译器不会检查参数数量、参数类型或不存在。 它在运行时验证这些东西,如果它无效,则抛出运行时异常。 请注意,动态类型不支持 智能提示

ExpandoObject 创建一个动态类型

使用ExpandObject类型我们可以创建一个弱类型语言一样,完全动态赋值的对象。

class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // 当这个person是employee的时候下面的代会有异常,因为没有这个属性
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
在上面的代码中我们也可以看到这个对象只要有相同的属性,都可以调用WritePerson方法,
哪我们就可以用dyamic来做一些事情。(不推荐,因为维护起来会比较麻烦了)

### 列举和删除成员
列举出所有的成员
``` csharp
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;

foreach (var property in (IDictionary<String, Object>)employee)
{
    Console.WriteLine(property.Key + ": " + property.Value);
}

删除成员

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
((IDictionary<String, Object>)employee).Remove("Name");

替代反射

在unittest的时候有时候我们会碰到一些对象是匿名类型。这样我们进行断言可能就得需要用反射,或者Json的方式。比较不方便。 这时候我们就可以用dynamic的方式来处理它。

 [Fact]
public void Age_Should_Be_1()
{
    dynamic s = GetAnoymousObject();
    int actual = (int)s.Age;
    Assert.Equal(1, actual);
}

public static object GetAnoymousObject()
{
    return new { Age = 1, Name = "abc" };
}

借助ExposedObject访问私有属性

有时候我们还会去访问私有属性。同样我们可以通过反射来访问。 但是我们也可以使用ExposedObject 来访问私有的属性。 (这个类库需要用Nuget来安装) 如下代码

class Program
{
    static void Main(string[] args)
    {
        var student = new Student();
        dynamic s = ExposedObject.Exposed.From(student);
        Console.WriteLine(s.ID);
    }
}


public class Student
{
    public Student()
    {
        ID = 500;
    }
    private int ID { get; set; }
}

动态语言运行时 (DLR) API 提供支持 C# 中的动态类型的基础结构。 有关 DLR 的更多信息,请访问动态语言运行时概述。

上一篇:C# 匿名类型
下一篇:C# 可空类型
最近更新的
...