可以中英切换的网站怎么做,四川住房建设厅官方网站,餐饮品牌vi设计开题报告,wordpress emoji表情翻译自 Camilo Reyes 2018年10月15日的文章 《Working with the Dynamic Type in C#》 [1] .NET 4 中引入了动态类型。动态对象使您可以处理诸如 JSON 文档之类的结构#xff0c;这些结构的组成可能要到运行时才能知道。在本文中#xff0c;Camilo Reyes 解释了如何使用动态… 翻译自 Camilo Reyes 2018年10月15日的文章 《Working with the Dynamic Type in C#》 [1] .NET 4 中引入了动态类型。动态对象使您可以处理诸如 JSON 文档之类的结构这些结构的组成可能要到运行时才能知道。在本文中Camilo Reyes 解释了如何使用动态类型。.NET 4.0 中引入的 dynamic 关键字为 C# 编程带来了一个范式转变。对于 C# 程序员来说强类型系统之上的动态行为可能会让人感到不适 —— 当您在编译过程中失去类型安全性时这似乎是一种倒退。动态编程可能使您面临运行时错误。声明一个在执行过程中会发生变化的动态变量是可怕的当开发人员对数据做出错误的假设时代码质量就会受到影响。对 C# 程序员来说避免代码中的动态行为是合乎逻辑的具有强类型的经典方法有很多好处。通过类型检查得到的数据类型的良好反馈对于正常运行的程序是至关重要的一个好的类型系统可以更好地表达意图并减少代码中的歧义。随着动态语言运行时Dynamic Language RuntimeDLR的引入这对 C# 意味着什么呢.NET 提供了丰富的类型系统可用于编写企业级软件。让我们来仔细看看 dynamic 关键字并探索一下它的功能。类型层次结构公共语言运行时Common Language RuntimeCLR中的每种类型都继承自 System.Object现在请重复阅读这句话直到将其铭记于心。这意味着 object 类型是整个类型系统的公共父类。当我们研究更神奇的动态行为时这一事实本身就能为我们提供帮助。这里的想法是开发这种“代码感”以便于您了解如何驾驭 C# 中的动态类型。为了演示这一点您可以编写以下程序Console.WriteLine(long inherits from ValueType: typeof(long).IsSubclassOf(typeof(ValueType)));
我将忽略 using 语句直到本文结束以保持对代码示例的专注。然后我再介绍每个命名空间及其作用。这样我就不必重复说过的话并提供了一个回顾所有类型的机会。上面的代码在控制台中的运算结果为 True。.NET 中的 long 类型是值类型因此它更像是枚举或结构体。ValueType 重写来自 object 类的默认行为。ValueType 的子类在栈stack上运行它们的生命周期较短效率更高。要验证 ValueType 是继承自 System.Object 的请执行以下代码Console.WriteLine(ValueType inherits from System.Object: typeof(ValueType).IsSubclassOf(typeof(Object)));
它的运算结果为 True。这是一条可以追溯到 System.Object 的继承链。对于值类型链中至少有两个父级。再看一下从 System.Object 派生的另一个 C# 类型例如Console.WriteLine(string inherits from System.Object: typeof(string).IsSubclassOf(typeof(Object)));
此代码在控制台中显示为 True。另一种从 object 继承的类型是引用类型引用类型在堆heap上分配并进行垃圾回收CLR 管理着引用类型并在必要时从堆中释放它们。查看下图您可以直观地看到 CLR 的类型系统值类型和引用类型都是 CLR 的基本构建块这种优雅的类型系统在 .NET 4.0 和动态类型之前就有了。我建议您在使用 C# 中的类型时在脑海中记住这张图。那么DLR 是如何适应这张图的呢?动态语言运行时DLR动态语言运行时Dynamic Language Runtime, DLR是处理动态对象的一种便捷方法。比如假设您有 XML 或 JSON 格式的数据其中的成员事先并不知道。DLR 允许您使用自然代码来处理对象和访问成员。对于 C#这使您可以处理在编译时不知道其类型的库。动态类型消除了自然 API 代码中的万能字符串。这就开启了像 IronPython 一样位于 CLR 之上的动态语言。可以将 DLR 视为支持三项主要服务表达式树来自 System.Linq.Expressions 命名空间。编译器在运行时生成具有动态语言互操作性的表达式树。动态语言超出了本文的讨论范围这里就不作介绍了。调用站点缓存即缓存动态操作的结果。DLR 缓存像 a b 之类的操作并存储 a 和 b 的特征。当执行动态操作时DLR 将检索先前操作中可用的信息。动态对象互操作性是可用于访问 DLR 的 C# 类型。这些类型包括 DynamicObject 和 ExpandoObject。可用的类型还有很多但是在处理动态类型时请注意这两种类型。要了解 DLR 和 CLR 是如何结合在一起的请看下图DLR 位于 CLR 之上。回想一下我说过的每种类型都是从 System.Object 派生而来的。嗯这句话对于 CLR 是适用的但是对于 DLR 呢我们使用下面的程序来测试一下这个理论Console.WriteLine(ExpandoObject inherits from System.Object: typeof(ExpandoObject).IsSubclassOf(typeof(Object)));Console.WriteLine(DynamicObject inherits from System.Object: typeof(DynamicObject).IsSubclassOf(typeof(Object)));
ExpandoObject 和 DynamicObject 在命令行中输出的值都是 True。可以将这两个类视为使用动态类型的基本构建块它们清楚地描绘了两个运行时是如何结合在一起的。一个 JSON 序列化程序动态类型解决的一个问题是当您有一个不知道其成员的 JSON HTTP 请求时假设要在 C# 中使用此任意的 JSON。要解决这个问题请将此 JSON 序列化为 C# 动态类型。我将使用 Newtonsoft 序列化库您可以通过 NuGet 添加此依赖项例如dotnet add package Newtonsoft.Json –-version 11.0.2
您可以使用这个序列化程序来处理 ExpandoObject 和 DynamicObject。探索每种动态类型给动态编程带来了什么。ExpandoObject 动态类型ExpandoObject 是一种方便的类型允许设置和检索动态成员。它实现了 IDynamicMetaObjectProvider该接口允许在 DLR 中的语言之间共享实例。因为它实现了 IDictionary 和 IEnumerable所以它也可以处理 CLR 中的类型。举例来说它允许将 ExpandoObject 的实例转换为 IDictionary然后像其它任意的 IDictionary 类型一样枚举成员。要用 ExpandoObject 处理任意 JSON您可以编写以下程序var exObj JsonConvert.DeserializeObjectExpandoObject({\a\:1}) as dynamic;Console.WriteLine($exObj.a {exObj?.a}, type of {exObj?.a.GetType()});
//exObj.a 1, type of System.Int64
它将会在控制台打印 1 和 long。请注意尽管它是一个动态 JSON但它会绑定到 CLR 中的 C# 类型。由于数字的类型未知因此序列化程序默认会选择最大的 long 类型。注意我成功地将序列化结果转换成了具有 null 检查的 dynamic 类型其原因是序列化程序返回来自 CLR 的 object 类型。因为 ExpandoObject 继承自 System.Object所以可以被拆箱成 DLR 类型。更奇妙的是可以用 IDictionary 枚举 exObjforeach (var exObjProp in exObj as IDictionarystring, object ?? new Dictionarystring, object())
{Console.WriteLine($IDictionary {exObjProp.Key}: {exObjProp.Value});
}
它在控制台中输出 IDictionary a: 1。请确保使用 string 和 object 作为键和值的类型。否则将在转换的过程中抛出 RuntimeBinderException 异常。DynamicObject 动态类型DynamicObject 提供对动态类型的精确控制。您可以继承该类型并重写动态行为。例如您可以定义如何设置和获取类型中的动态成员。DynamicObject 允许您通过重写选择实现哪些动态操作。这比实现 IDynamicMetaObjectProvider 的语言实现方式更易访问。它是一个抽象类需要继承它而不是实例化它。该类有 14 个虚方法它们定义了类型的动态操作每个虚方法都允许重写以指定动态行为。假设您想要精确控制动态 JSON 中的内容。尽管事先不知道其属性您却可以使用 DynamicObject 来控制类型。让我们来重写三个方法TryGetMember、TrySetMember 和 GetDynamicMemberNamespublic class TypedDynamicJsonT : DynamicObject
{private readonly IDictionarystring, T _typedProperty;public TypedDynamicJson(){_typedProperty new Dictionarystring, T();}public override bool TryGetMember(GetMemberBinder binder, out object result){T typedObj;if (_typedProperty.TryGetValue(binder.Name, out typedObj)){result typedObj;return true;}result null;return false;}public override bool TrySetMember(SetMemberBinder binder, object value){if (value.GetType() ! typeof(T)){return false;}_typedProperty[binder.Name] (T)value;return true;}public override IEnumerablestring GetDynamicMemberNames(){return _typedProperty.Keys;}
}
C# 泛型强类型 _typedProperty 以泛型的方式驱动成员类型。这意味着其属性类型来自泛型类型 T。动态 JSON 成员位于字典中并且仅存储泛型类型。此动态类型允许同一类型的同类成员集合。尽管它允许动态成员集但您可以强类型其行为。假设您只关心任意 JSON 中的 long 类型var dynObj JsonConvert.DeserializeObjectTypedDynamicJsonlong({\a\:1,\b\:\1\}) as dynamic;
Console.WriteLine($dynObj.a {dynObj?.a}, type of {dynObj?.a.GetType()});var members string.Join(,, dynObj?.GetDynamicMemberNames());
Console.WriteLine($dynObj member names: {members});
结果是您将看到一个值为 1 的属性因为第二个属性是 string 类型。如果将泛型类型更改为 string将会获得第二个属性。类型结果到目前为止已经涉及了相当多的领域; 以下是一些亮点CLR 和 DLR 中的所有类型都继承自 System.ObjectDLR 是所有动态操作发生的地方ExpandoObject 实现了 CLR 中诸如 IDictionary 的可枚举类型DynamicObject 通过虚方法对动态类型进行精确控制看一下在控制台的结果截图单元测试对于单元测试我将使用 xUnit 测试框架。在 .NET Core 中您可以使用 dotnet new xunit 命令添加一个测试项目。一个显而易见的问题是模拟和验证动态参数例如假设您想验证一个方法调用是否具有动态属性。要使用 Moq 模拟库您可以通过 NuGet 添加此依赖项例如dotnet add package Moq –-version 4.10.0
假设您有一个接口其想法是验证它是否被正确的动态对象调用。public interface IMessageBus
{void Send(dynamic message);
}
忽略该接口的实现。这些实现细节对于编写单元测试不是必需的。下面是被测试的系统public class MessageService
{private readonly IMessageBus _messageBus;public MessageService(IMessageBus messageBus){_messageBus messageBus;}public void SendRawJsonT(string json){var message JsonConvert.DeserializeObjectT(json) as dynamic;_messageBus.Send(message);}
}
您可以使用泛型这样就可以为序列化程序传入动态类型。然后调用 IMessageBus 并发送动态消息。被测试的方法接受一个 string 参数并使用 dynamic 类型进行调用。对于单元测试请将其封装在 MessageServiceTests 类中。首先初始化 Mock 和被测试的服务public class MessageServiceTests
{private readonly MockIMessageBus _messageBus;private readonly MessageService _service;public MessageServiceTests(){_messageBus new MockIMessageBus();_service new MessageService(_messageBus.Object);}
}
使用 Moq 库中的 C# 泛型来模拟 IMessageBus然后使用 Object 属性创建一个模拟实例。在所有的单元测试中私有实例变量都很有用高可重用性的私有实例增加了类的内聚性。使用 Moq 验证调用一种直观的方式是尝试这么做_messageBus.Verify(m m.Send(It.IsExpandoObject(o o ! null (o as dynamic).a 1)));
但是遗憾的是您将看到这样的错误消息“表达式树不能包含动态操作。” 这是因为 C# lambda 表达式无法访问 DLR它期望一个来自 CLR 的类型这使得此动态参数难以验证。记得您的训练利用您的“代码感”来解决这个问题。要处理诸如类型之间不一致的问题请使用 Callback 方法dynamic message null;_messageBus.Setup(m m.Send(It.IsAnyExpandoObject())).Callbackobject(o message o);
请注意Callback 方法将类型转换为 System.Object。因为所有类型都继承自 object 类型所以可以将其赋值为 dynamic 类型。C# 可以把此 lambda 表达式中的 object 拆箱成 dynamic message。是时候为 ExpandoObject 类型编写一个漂亮的单元测试了。使用 xUnit 作为测试框架您将看到带有 Fact 属性的方法。[Fact]
public void SendsWithExpandoObject()
{// arrangeconst string json {\a\:1};dynamic message null;_messageBus.Setup(m m.Send(It.IsAnyExpandoObject())).Callbackobject(o message o);// act_service.SendRawJsonExpandoObject(json);// assertAssert.NotNull(message);Assert.Equal(1, message.a);
}
使用 DynamicObject 类型进行测试重用您之前看到的 TypedDynamicJson[Fact]
public void SendsWithDynamicObject()
{// arrangeconst string json {\a\:1,\b\:\1\};dynamic message null;_messageBus.Setup(m m.Send(It.IsAnyTypedDynamicJsonlong())).Callbackobject(o message o);// act_service.SendRawJsonTypedDynamicJsonlong(json);// assertAssert.NotNull(message);Assert.Equal(1, message.a);Assert.Equal(a, string.Join(,, message.GetDynamicMemberNames()));
}
使用 C# 泛型您可以在重用代码的同时转换序列化程序的动态类型。Moq 中的 Callback 方法允许您在两种类型系统之间进行必要的跳转。拥有一个优雅的类型层次结构和一个共同的父类成为了一个救星。Using 语句下面的 using 语句是代码示例的一部分System: CLR 的基础类型例如 Object 和 ConsoleSystem.Collections.Generic: 可枚举类型例如 IDictionarySystem.Dynamic: DLR 的动态类型例如 ExpandoObject 和 DynamicObjectNewtonsonft.Json: JSON 序列化程序Moq: 模拟库Xunit: 测试框架总结C# 动态类型或许看起来令人望而生畏但它在强类型系统之上有很多好处。DLR 是所有动态操作发生和与 CLR 交互的地方类型继承使同时处理这两个类型系统变得容易。在 C# 中动态和静态编程之间并没有对立这两种类型系统共同协作以创造性的方式解决动态问题。相关链接https://www.red-gate.com/simple-talk/dotnet/c-programming/working-with-the-dynamic-type-in-c/ Working with the Dynamic Type in C# ↩︎作者 Camilo Reyes 译者 技术译民 出品 技术译站https://ITTranslator.cn/END