目录

扰流板

什么是动态的

让我们创建自己的DynamicObject

包装起来


扰流板

GitHub上提供了该项目的完整源代码。repo包含卡片图像的完整生成器,我将其命名为Card Artist。棋盘游戏设计师可以使用它来创建自己的纸牌以进行游戏测试或专业印刷。GitHub存储库还包含该应用程序的二进制文件以及有关如何创建自己的卡片模板的文档。

什么是动态的

Microsoft在2010年发布了动态语言运行时(DLR),作为.NET Framework版本4的一部分。DLR允许在.NET CLR之上支持动态语言,例如Python。那时,动态类型也被添加到C#中,从而允许与动态语言进行互操作。

动态类型的变量类似于对象类型的变量:

  • 它不包含有关其引用的对象类型的任何信息。
  • 它可以保存对任何类型的对象的引用。

与对象不同,动态类型的变量可用于调用方法并访问其类型的属性和字段,而无需强制转换:

class Class1
{public int ReturnValue(int a, int b) => a + b;public int A = 5;
}var typedReference = new Class1();
var a2 = typedReference.A;
var b2 = typedReference.ReturnValue(1, 2);//Now with object
object objectReference = new Class1();
var a2 = ((Class1)objectReference).A;
var b2 = ((Class1)objectReference).ReturnValue(1, 2);//Now with dynamic
dynamic dynamicReference = new Class1();
int a3 = dynamicReference.A;
int b3 = dynamicReference.ReturnValue(1, 2);//This compiles successfully and fails at runtime
dynamicReference.ThisFunctionDoesntExist();

请注意,我们需要指定类型a3和b3,因为编译器没有关于该字段的类型的信息A和方法ReturnValue的返回类型。编译器甚至不知道是否存在这样的字段和方法,正如它不会抱怨我们调用的事实所证明的ThisFunctionDoesntExist()。如果我们没有为a3和b3指定类型(而是使用var),它们也将是动态类型的变量。在任何时候,我们都可以将动态类型的变量强制转换为其实际类型。

我本人满怀激情地讨厌动态语言:为什么您会放弃让编译器为您查找错误的优势,而只是避免编写更多的代码行?因此,很少在C#中看到动态的用法。

尽管在编译时无法访问类型,但是使用动态更有意义。通常,您将使用反射:

var type = Assembly.GetExecutingAssembly().GetType("MyNamespace.Class1");
var referenceToUnknowType = type.GetConstructor(Array.Empty<Type>()).Invoke(Array.Empty<object>());
var a4 = (int)t.GetField("A").GetValue(c);
var b4 = (int)t.GetMethod("ReturnValue", new Type[] { typeof(int), typeof(int) }).Invoke(c, new object[] { 1, 2 });

这不漂亮!

如果您想知道为什么会需要这样的东西,那么在编写支持插件的应用程序时通常会使用反射。由于插件是与应用程序分开编写的,因此在编译应用程序本身时不能使用插件的类型。

另一个用例是访问private对象的方法和成员(ugh!)。

动态允许以更易读的方式编写相同的代码:

var type = Assembly.GetExecutingAssembly().GetType("MyNamespace.Class1");
var dynamicReferenceToUnknownType = type.GetConstructor(Array.Empty<Type>()).Invoke(Array.Empty<object>());
int a4 = dynamicReferenceToUnknownType.A;
int b4 = dynamicReferenceToUnknownType.ReturnValue(1, 2);

这与使用反射一样安全,因为反射也不提供任何编译时验证,但是可读性更高。

值得注意的是,反射代码比“普通”代码(上面使用的typedReference代码)慢数百倍。动态也因其速度较慢而臭名昭著:在这种情况下,它比使用反射要快一点,但仍比静态类型的替代要慢数百倍。

动态类型还可以用于轻松访问其他非静态类型的数据,例如COM对象(此处有关此信息)或JSON文件。

让我们创建自己的DynamicObject

如前所述,动态对象的语法非常简单干净,但是使用它们的速度却很慢且容易出错。之所以缓慢,是因为DLR每次都必须使用其名称搜索对象的“成员”,危险来自可能找不到成员。

因此,动态对象非常适合暴露具有相同“缓慢而危险”特征的操作。解析XML文件就是这样的操作之一:

  • 这很慢,因为必须解析XML语言,并且元素和属性名称必须与我们的查询匹配,
  • 这是有风险的,因为C#编译器不能保证XML文件遵守预期的架构(该文件甚至不是应用程序的一部分,所以编译器可以做什么?)。

此外,动态对象不提供任何智能感知支持。但是,无论我们使用哪种技术,我们都永远不会获得遍历XML数据结构的任何智能感知支持,因此在此也不会丢失任何信息。

通常,为了使用C#访问XML数据,我们需要编写如下代码:

var xml = @"<Characters><Batman Age=""81""><Equipment><Item>Batarangs</Item><Item>Shark repellent</Item></Equipment></Batman><Robin Age=""37""><Equipment><Item>Red hood</Item></Equipment></Robin>
</Characters>";var characters = XDocument.Parse(xml).Root;
var batmanAge = int.Parse(characters.Element("Batman").Attribute("Age").Value);
var batarangs = characters.Element("Batman").Element("Equipment").Elements().First().Value;

通过将XML数据公开为动态对象,我们可以实现更具可读性的语法。这可以通过创建一个新类并扩展DynamicObject来完成。

class XmlDynamicElement : DynamicObject
{private readonly XElement Element;public XmlDynamicElement(XElement element){Element = element;}...
}dynamic characters = new XmlDynamicElement(XDocument.Parse(xml).Root);
int batmanAge = characters["Batman", 0].Age;
string batarangs = characters["Batman", 0]["Equipment", 0][0];

Dynamic Duo:

XML和DynamicObject。

首先,我们的新XmlDynamicElement类应支持使用[]运算符访问子元素。Element[int]将返回第n个孩子。Element[string, int]将返回具有指定名称的第n个元素。这实际上很容易实现:

class XmlDynamicElement : DynamicObject
{public override bool TryGetIndex(GetIndexBinder binder, object[] indexes,out object result){result = null;XElement childElement;if (indexes.Length == 1 &&indexes[0] is int index){childElement = Element.Elements().ElementAtOrDefault(index);}else if (indexes.Length == 1 &&indexes[0] is string name){result = Element.Elements(name).Select(e => new XmlDynamicElement(e)).ToArray();return true;}else if (indexes.Length == 2 &&indexes[0] is string name2 &&indexes[1] is int index2){childElement = Element.Elements(name2).ElementAtOrDefault(index2);}elsethrow new ArgumentException("Invalid index type");if (childElement == null)throw new IndexOutOfRangeException();result = new XmlDynamicElement(childElement);return true;}
...

在上面的代码中,我还添加了对Element[string]的支持,返回包含具有指定名称的元素的数组。我没有返回IEnumerable,因为扩展方法不能很好地适应动态环境。我真的不需要处理异常,这些异常只会在用户使用无效索引时浮出水面。

接下来,我们希望将一个XmlDynamicElement自动转换为一个string或数字。如果我们想取回XElement,那就更好了。

class XmlDynamicElement : DynamicObject
{public override bool TryConvert(ConvertBinder binder, out object result){if (binder.Type == typeof(String))result = ToString();else if (binder.Type == typeof(XElement))result = Element;elseresult = Convert.ChangeType(ToString(), binder.Type,CultureInfo.InvariantCulture);return true;}public override string ToString() =>Element.Nodes().Aggregate(new StringBuilder(),(sb, n) => sb.Append(n.ToString())).ToString();
...

为了保持连贯性,我将定义到string的转换,以返回与提供元素连接内容的.ToString()方法相同的值。我还利用Convert.ChangeType轻松地支持默认转换为多种类型。

XmlDynamicElement实现的最后一点是允许访问属性。因为XML元素不能具有两个具有相同名称的属性,所以我们可以将特性表示为属性:

class XmlDynamicElement : DynamicObject
{public override bool TryGetMember(GetMemberBinder binder, out object result){var attribute = Element.Attribute(binder.Name);result = attribute != null ? new XmlDynamicAttribute(attribute) : null;return attribute != null;}public override IEnumerable<string> GetDynamicMemberNames() =>Element.Attributes().Select(a => a.Name.ToString());
...

然后,用非常相似的DynamicObject表示属性:

class XmlDynamicAttribute : DynamicObject
{private readonly XAttribute Attribute;public XmlDynamicAttribute(XAttribute attribute){Attribute = attribute;}public override bool TryConvert(ConvertBinder binder, out object result){if (binder.Type == typeof(String))result = ToString();else if (binder.Type == typeof(XAttribute))result = Attribute;else    result = Convert.ChangeType(ToString(), binder.Type,CultureInfo.InvariantCulture);return true;}public override string ToString() =>return Attribute.Value;
}

我们可能想在类中添加一些额外的功能作为方法,例如,访问元素名称或其所有子元素可能很有用。

class XmlDynamicElement : DynamicObject
{public string Xml() =>Element.ToString();public XmlDynamicElement[] Elements() =>Element.Elements().Select(e => new XmlDynamicElement(e)).ToArray();public XmlDynamicAttribute[] Attributes() =>Element.Attributes().Select(a => new XmlDynamicAttribute(a)).ToArray();
...

不幸的是,当XmlDynamicElement对象位于动态引用之后时,这些方法将无法访问。为了解决这个问题,我们需要实现TryInvokeMember:

class XmlDynamicElement : DynamicObject
{public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args,out object result){if (args == null || args.Length == 0){switch (binder.Name){case "Xml":result = Xml();return true;case "Elements":result = Elements();return true;case "Attributes":result = Attributes();return true;}}result = null;return false;}
...

包装起来

这种方法会对性能产生巨大影响。基于一些粗浅的测试,我可以看到,这个实现比直接使用XElement和XAttribute慢约20倍。简化语法要付出巨大的代价!

但有时可用性是最重要的。在几篇文章中,我们将使用XmlDynamicElement允许用户从模板化的XAML文档引用XML数据,这正是这种情况。因为我们会要求用户使用c#和XAML的奇怪组合来编写XML查询(如果等不及就去查Razor),所以我们真的希望尽可能降低增加的复杂性。

创建您自己的.NET DynamicObject 为什么、何时和如何相关推荐

  1. 使用DynamicObject的自定义动态行为

    介绍 DynamicObject类使开发人员能够实现动态类型或在运行时动态行为.开发人员无法直接创建此类的实例.相反,他需要扩展类以使其具有所需的行为.例如,对于具有动态行为的类,请参见下面的最小步骤 ...

  2. 用C#实现仿Ruby的XML Builder

    Ruby中有个很好用的XML API,在页面实现XML输出很方便.比如有个联系人Contacts的XML输出,Ruby里面是这样写的. ... xml.contacts doxml.contact(: ...

  3. 记录金蝶苍穹开发代码实例

    隐藏元素  setVisible(true=显示 false=隐藏,标识) this.getView().setVisible(false, new String[]{"标识"}) ...

  4. git的详细用法和基础教程

    Git 是当前最流行的版本控制程序之一,文本包含了 Git 的一些基本用法 创建 git 仓库 初始化 git 仓库 mkdir project  # 创建项目目录  cd project  # 进入 ...

  5. [K/3Cloud] 创建一个单据转换插件

    概念: 创建一个业务单据转换插件,在单据转换的各个时点干预单据转换的相关逻辑控制. 示例: 新建一个类,继承自单据转换插件基类Kingdee.BOS.Core.Metadata.ConvertElem ...

  6. 第二章 Caché JSON 创建和修改动态实体

    文章目录 第二章 Caché JSON 创建和修改动态实体 使用JSON文字构造器 使用动态表达式和点语法 使用点语法创建动态对象属性 使用点语法创建动态数组元素 使用 %Set(), %Get(), ...

  7. CityMaker学习教程08 一个示例,创建漫游路径

    1.在看完CS代码后可以自己写程序了,我刚写了一个由线创建动态路径的代码,给大家参考. 2.配置完场景,加载完FDB. 3.代码片段 _axcontrol.axRenderControl1.Inter ...

  8. 创建数据库,指定数据库的字符集和编码顺序

    创建数据库,指定它的字符集和编码顺序 create database {数据库名称} CHARACTER SET {字符集} COLLATE {排序规则} 举例: create database co ...

  9. Linux下创建硬链接,文件访问为空,提示:xxxx: 符号连接的层数过多

    Linux下创建软链接|硬链接,文件访问为空,提示:x x x: 符号连接的层数过多. 原因:创建符号链接的时候未使用绝对路径,无论是源文件路径还是目标路径,都需要使用绝对路径. 如: ln -s / ...

最新文章

  1. 《评人工智能如何走向新阶段》后记(再续16)
  2. 缺陷大扫除(Bug Bash)
  3. Machine Learning机器学习公开课汇总
  4. Memcached 客户端使用
  5. .ipynb转化为.py文件后批量删除一大堆#In[53]
  6. java取非_java运算符 与()、非(~)、或(|)、异或(^)
  7. 【Linux_Fedora_系统管理系列】_1_用户登录和系统初始配置
  8. HTML5与搜索引擎优化[转载]
  9. goland 方法注释_goland 设置注释模板的过程图文详解
  10. 开发sharepoint工作流过程中的Ptifall(容易犯的错误)
  11. Nginx的Upstream负载均衡模块
  12. 手机在线编程软件Anycodes
  13. java处理json_Java处理JSon方法
  14. Thor HTTP 抓包嗅探分析接口调试网络协议
  15. GAMP读取精密星历存在一个小BUG
  16. 《东京食尸鬼》观后感
  17. 世界上最简单的会计书(先进先出法VS后进先出法)
  18. 不知道什么工具可以转换图片格式?好用的图片格式转换工具分享
  19. 高大上~的裸眼3D原理与制作方法
  20. Semi-Supervised Semantic Segmentation with Cross-Consistency Training论文笔记

热门文章

  1. 如何去选取第一批要阅读的论文?_顶会最佳论文奖得主:初入科研领域,如何正确做科研?...
  2. 传输层端口号的范围是多少?被分为哪两部分_第三章, 传输层
  3. plc有与计算机的通讯请求时,OMRON PLC网络Ethernet网_OMRON PLC与上位计算机之间的通信_OMRON PLC与上位计算机通信程序-维库电子通...
  4. android+php最佳实践视频,Android和PHP开发最佳实践 PDF 第2版
  5. 插画素材模板 | 玩转电商促销季插画设计
  6. 电商手机端促销海报设计PSD分层模板,来给你保驾护航!
  7. 建议把英语改成选修的计算机老师,中小学“变动”,英语改为副科?老师没意见家长却愁眉不展...
  8. destools php_php DES加密算法实例分析
  9. QJsonObject与QString的相互转换(处理了中文乱码)
  10. Python自己写模块提供调用