You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PolyglotNotebooksStudy/Docs/多语言笔记.1.2.显示格式化.ipynb

1298 lines
54 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"cell_type": "markdown",
"id": "40dc2502",
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"单元格输出的格式化\n",
"================"
]
},
{
"cell_type": "markdown",
"id": "1dcd22f8",
"metadata": {},
"source": [
"## 格式化输出"
]
},
{
"cell_type": "markdown",
"id": "5ba9817e",
"metadata": {},
"source": [
"基于 .NET Interactive 的工具(包括 Polyglot 笔记本、Jupyter 和其他工具)的输出是通过 .NET Interactive 格式化器生成的,这是一组位于 Microsoft.DotNet.Interactive.Formatting 命名空间下的 API。这些 API 可以作为一个 NuGet 包独立于笔记本使用。\n",
"格式化器创建对象的字符串表示,这些字符串表示可以从纯文本到 HTML再到像 JSON 和 CSV 这样的机器可读格式。\n",
"\n",
"以下是一些你可以在笔记本中编写的代码示例,这些代码会导致对象被格式化以供显示:\n",
"- C# 单元格末尾的返回语句或尾随表达式\n",
"- F# 单元格末尾的尾随表达式\n",
"- 调用 Display 和 ToDisplayString 扩展方法,这些方法对 C# 和 F# 中的所有对象都可用\n",
"- 在 PowerShell 单元格中调用 Out-Display\n",
"\n",
"格式化器还用于在多语言笔记本变量视图中格式化.NET对象的输出显示。其他语言的值格式化并不依赖于.NET。\n",
"> `格式化` 指的是创建对象字符串表示的过程。此过程由.NET Interactive内核通过此处所述的API实现。当格式化后的字符串在 VS Code 或 JupyterLab 中的笔记本上显示时,这一过程被称为`渲染`"
]
},
{
"cell_type": "markdown",
"id": "bfda8369",
"metadata": {},
"source": [
"## MIME类型 与 Display显示"
]
},
{
"cell_type": "markdown",
"id": "3800f1fa",
"metadata": {},
"source": [
"对于任何一个给定的对象可以有多种不同的字符串表示方式。这些不同的表示方式都有对应的MIME类型这些类型由简短的字符串标识例如`text/html`或`application/json`。MIME类型可以用来通过Display扩展方法请求特定格式的对象显示这个方法可以用于任何对象。\n",
"\n",
"例如我们可以调用rect.Display()来显示分配给变量rect的Rectangle对象"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#27+Rectangle</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Width</td><td><div class=\"dni-plaintext\"><pre>100</pre></div></td></tr><tr><td>Height</td><td><div class=\"dni-plaintext\"><pre>50</pre></div></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#27+Rectangle</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Width</td><td><div class=\"dni-plaintext\"><pre>100</pre></div></td></tr><tr><td>Height</td><td><div class=\"dni-plaintext\"><pre>50</pre></div></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"//注意System.Drawing不能跨平台只在Windows系统中使用。\n",
"//不知道为啥:官方要选个不跨平台的示例\n",
"using System.Drawing;\n",
"var ract = new Rectangle\n",
"{\n",
" Height = 50,\n",
" Width = 100,\n",
"};\n",
"\n",
"//默认为 text/html下面两种方式的效果是一样的\n",
"ract.Display();\n",
"ract.Display(\"text/html\");"
]
},
{
"cell_type": "markdown",
"id": "3bc132ae",
"metadata": {},
"source": [
"注意Polyglot 笔记本中默认的 MIME 类型是 text/html。这可能会因 .NET 类型的不同而有所变化,但在上述示例中,矩形类型尚未应用任何自定义设置。(将在下面展示更多关于如何进行自定义设置的内容。)\n",
"> 注意:对于 C# 或 F# 中单元格的返回值,只能使用默认 MIME 类型的格式化程序。"
]
},
{
"cell_type": "markdown",
"id": "4d2d0a0a",
"metadata": {},
"source": [
"在使用Display时可以指定不同于默认的MIME类型。只需将所需的MIME类型作为参数传递即可例如rect.Display(\"text/plain\")"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Rectangle\r\n",
" Width: 100\r\n",
" Height: 50"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ract.Display(\"text/plain\");"
]
},
{
"cell_type": "markdown",
"id": "62941e56",
"metadata": {},
"source": [
"另一种常见的MIME类型是application/json。在Polyglot笔记本中使用此MIME类型时对象使用System.Text.Json进行格式化。\n",
"\n",
"执行下面的单元格会以Json格式输出。JSON输出中的代码颜色由 VS Code 笔记本渲染器针对`application/json`类型提供。"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"application/json": {
"Height": 50,
"Width": 100
}
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ract.Display(\"application/json\");"
]
},
{
"cell_type": "markdown",
"id": "c69d8a8e",
"metadata": {},
"source": [
"## 自定义格式化"
]
},
{
"cell_type": "markdown",
"id": "d65a2d3a",
"metadata": {},
"source": [
".NET Interactive的格式化API是高度可配置的。\n",
"\n",
"可以使用`Microsoft.DotNet.Interactive.Formatting`中的代码,调整格式化行为的方式。"
]
},
{
"cell_type": "markdown",
"id": "86f3a920",
"metadata": {},
"source": [
"### 限制输出数量\n",
"在格式化序列时例如数组或实现IEnumerable的对象.NET Interactive的格式化器会将它们展开以便你可以看到其中的值。\n",
"\n",
"如果执行结果集太多,默认情况下只会输出前面很少项,剩余的数据以 `...More` 代替。"
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 1, Age = 7, Name = 韦子异 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>1</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>7</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>韦子异</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 2, Age = 18, Name = 姚宇宁 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>2</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>18</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>姚宇宁</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 3, Age = 2, Name = 傅子异 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>3</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>2</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>傅子异</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 4, Age = 29, Name = 朱子韬 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>4</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>29</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>朱子韬</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>4</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 5, Age = 85, Name = 林杰宏 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>5</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>85</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>林杰宏</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>5</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 6, Age = 76, Name = 胡璐 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>6</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>76</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>胡璐</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>6</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 7, Age = 88, Name = 周璐 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>7</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>88</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>周璐</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>7</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 8, Age = 88, Name = 田秀英 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>8</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>88</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>田秀英</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>8</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 9, Age = 19, Name = 姜詩涵 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>9</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>19</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>姜詩涵</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>9</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 10, Age = 44, Name = 蔡秀英 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>10</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>44</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>蔡秀英</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>10</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 11, Age = 12, Name = 张睿 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>11</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>12</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>张睿</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>11</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 12, Age = 94, Name = 金嘉伦 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>12</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>94</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>金嘉伦</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>12</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 13, Age = 73, Name = 萧致远 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>13</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>73</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>萧致远</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>13</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 14, Age = 90, Name = 赵致远 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>14</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>90</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>赵致远</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>14</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 15, Age = 82, Name = 蒋宇宁 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>15</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>82</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>蒋宇宁</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>15</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 16, Age = 38, Name = 顾震南 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>16</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>38</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>顾震南</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>16</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 17, Age = 15, Name = 余安琪 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>17</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>15</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>余安琪</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>17</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 18, Age = 74, Name = 熊岚 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>18</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>74</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>熊岚</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>18</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 19, Age = 19, Name = 卢子韬 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>19</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>19</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>卢子韬</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>19</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 20, Age = 44, Name = 孔杰宏 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>20</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>44</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>孔杰宏</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td colspan=\"2\"><i>... (more)</i></td></tr></tbody></table><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"var names = new string[]\n",
"{\n",
" \"吕宇宁\", \"韦子异\", \"姚宇宁\", \"傅子异\", \"朱子韬\", \"林杰宏\", \"胡璐\", \"周璐\", \"田秀英\", \"姜詩涵\", \"蔡秀英\", \"张睿\", \"金嘉伦\", \"萧致远\", \"赵致远\", \"蒋宇宁\", \"顾震南\", \"余安琪\", \"熊岚\", \"卢子韬\",\n",
" \"孔杰宏\", \"周子韬\", \"黄睿\", \"史璐\", \"赵震南\", \"杜安琪\", \"赵致远\", \"石詩涵\", \"龚杰宏\", \"丁安琪\", \"黄杰宏\", \"傅睿\", \"戴嘉伦\", \"郝杰宏\", \"傅晓明\", \"孟嘉伦\", \"段睿\", \"戴致远\", \"石安琪\", \"汪詩涵\",\n",
" \"贾云熙\", \"邱子韬\", \"吴杰宏\", \"贾岚\", \"曾震南\", \"许云熙\", \"吴宇宁\", \"唐岚\", \"常嘉伦\", \"曾岚\", \"袁嘉伦\", \"黄晓明\", \"韦致远\", \"莫安琪\", \"丁子韬\", \"雷云熙\", \"许秀英\", \"朱宇宁\", \"黎詩涵\", \"贾晓明\", \n",
" \"孔詩涵\", \"秦宇宁\", \"方子韬\", \"邵秀英\", \"冯宇宁\", \"何晓明\", \"方嘉伦\", \"熊秀英\", \"沈云熙\", \"顾秀英\", \"许致远\", \"胡宇宁\", \"陶子异\", \"叶安琪\", \"邱震南\", \"刘子异\", \"周宇宁\", \"黄云熙\", \"龚杰宏\", \"杜秀英\", \n",
" \"向子异\", \"马睿\", \"黄安琪\", \"于安琪\", \"金嘉伦\", \"龚璐\", \"杨致远\", \"戴嘉伦\", \"钟詩涵\", \"阎詩涵\", \"雷安琪\", \"宋杰宏\", \"田致远\", \"冯致远\", \"雷杰宏\", \"雷子异\", \"叶璐\", \"王子异\", \"冯子韬\", \"史宇宁\"\n",
"};\n",
"\n",
"var students = Enumerable.Range(1,50).Select(s => new {Id = s, Age = Random.Shared.Next(1,100), Name = names[s]});\n",
"students.Display();"
]
},
{
"cell_type": "markdown",
"id": "e9e5f666",
"metadata": {},
"source": [
"Formatter.ListExpansionLimit 更改为自定义值"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 1, Age = 18, Name = 韦子异 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>1</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>18</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>韦子异</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 2, Age = 89, Name = 姚宇宁 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>2</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>89</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>姚宇宁</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 3, Age = 52, Name = 傅子异 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>3</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>52</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>傅子异</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 4, Age = 32, Name = 朱子韬 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>4</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>32</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>朱子韬</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>4</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Id = 5, Age = 73, Name = 林杰宏 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>5</pre></div></td></tr><tr><td>Age</td><td><div class=\"dni-plaintext\"><pre>73</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>林杰宏</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td colspan=\"2\"><i>... (more)</i></td></tr></tbody></table><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter.ListExpansionLimit = 5;\n",
"students.Display();"
]
},
{
"cell_type": "markdown",
"id": "55513f2a",
"metadata": {},
"source": [
"上面的示例中通过设置Formatter.ListExpansionLimit = 5然后显示相同的列表对象.NET Interactive现在仅显示前五项后面跟着 ...(more)。\n",
"\n",
"也可以通过设置`Formatter<T>.ListExpansionLimit`来限制特定类型的输出。需要注意的是这里的类型T必须与列表中的项完全匹配。\n",
"\n",
"以下是一个使用int类型的示例"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div class=\"dni-plaintext\"><pre>[ 1, 2, 3 ... (7 more) ]</pre></div><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter<int>.ListExpansionLimit = 3;\n",
"Enumerable.Range(1, 10).Display();\n"
]
},
{
"cell_type": "markdown",
"id": "c30384b0",
"metadata": {},
"source": [
"注意:有些以 ...more结尾有些以数字 more结尾。\n",
"\n",
"这是因为List、List<T>、数组等,列举前就知道元素确切的数量,以 `(数字 more)`结尾;而像 IEnumerable<T>(Enumerable.Range 返回类型是IEnumerable<T>)之类的,因此在枚举整个序列之前,无法知道元素确切的数量;在这种情况下,.NET交互式格式化器在达到配置的ListExpansionLimit时会停止并不会继续计数剩余的序列, 以 `...(more)` 结尾"
]
},
{
"cell_type": "markdown",
"id": "8ce3b66a",
"metadata": {},
"source": [
"### 限制对象循环引用次数"
]
},
{
"cell_type": "markdown",
"id": "79e36f3f",
"metadata": {},
"source": [
"有些对象存在引用循环。虽然.NET Interactive格式化器会遍历对象图但它为了避免输出过大和无限递归只会递归到特定深度。\n",
"\n",
"以下是一个C#代码示例它定义了一个简单的Node类创建了一个引用循环并使用C#脚本的尾随表达式(相当于返回语句)对其进行格式化:"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td>Submission#93+Node</td></tr></tbody></table></div></details></td></tr></tbody></table></div></details></td></tr></tbody></table></div></details></td></tr></tbody></table></div></details></td></tr></tbody></table></div></details></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"public class Node\n",
"{\n",
" public Node Next { get; set; } \n",
"}\n",
"\n",
"Node node1 = new();\n",
"Node node2 = new();\n",
"\n",
"node1.Next = node2;\n",
"node2.Next = node1;\n",
"\n",
"node1"
]
},
{
"cell_type": "markdown",
"id": "0cdad28f",
"metadata": {},
"source": [
"这表明格式化器在格式化到深度6后停止了递归。这个深度可以通过Formatter.RecursionLimit方法进行更改"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#93+Node</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Next</td><td>Submission#93+Node</td></tr></tbody></table></div></details></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter.RecursionLimit = 2;\n",
"node1"
]
},
{
"cell_type": "markdown",
"id": "9ae2d29c",
"metadata": {},
"source": [
"### 首选 MIME 类型"
]
},
{
"cell_type": "markdown",
"id": "210ac005",
"metadata": {},
"source": [
"前面提到Polyglot 笔记本中用于格式化的默认 MIME 类型是 text/html。当使用 Display() 方法时,如果没有向 mimeType 参数传递值,或者在 C# 或 F# 中使用 return 语句或尾随表达式时,就会应用这个默认设置。这个默认设置可以全局更改,也可以针对特定类型更改。\n",
"\n",
"以下示例将 Rectangle 的默认值更改为 text/plain。"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Student\r\n",
" Id: 1\r\n",
" Name: 张三\r\n",
" Age: 55"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"public class Student\n",
"{\n",
" public int Id {get;set;}\n",
" public string Name {get;set;}\n",
" public int Age {get;set;}\n",
"}\n",
"\n",
"Microsoft.DotNet.Interactive.Formatting.Formatter.SetPreferredMimeTypesFor(typeof(Student), \"text/plain\");\n",
"\n",
"new Student\n",
"{ \n",
" Id = 1,\n",
" Name = \"张三\",\n",
" Age = 55\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "e2d42a41",
"metadata": {},
"source": [
"该方法可用于设置多个首选MIME类型。第二个参数是params参数它允许您传递多个值。"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"application/json": {
"Age": 55,
"Id": 1,
"Name": "张三"
},
"text/plain": [
"Student\r\n",
" Id: 1\r\n",
" Name: 张三\r\n",
" Age: 55"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter.SetPreferredMimeTypesFor\n",
"(\n",
" typeof(Student), \n",
" \"text/plain\",\n",
" \"application/json\"\n",
");\n",
"\n",
"new Student\n",
"{ \n",
" Id = 1,\n",
" Name = \"张三\",\n",
" Age = 55\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "24305cfd",
"metadata": {},
"source": [
"注册多个MIME时可以切换输出格式\n",
"单击 执行结果单元格的最左侧`...`, 选择 `更改演示文稿`后在VS Code最上访会弹出选择窗口选择注册项中的一个就可以重新以选中项的格式重新显示结果。"
]
},
{
"cell_type": "markdown",
"id": "058b472e",
"metadata": {},
"source": [
"### 替换默认的格式化类型"
]
},
{
"cell_type": "markdown",
"id": "15eeab9b",
"metadata": {},
"source": [
"默认格式化器通常通过打印列表和属性来显示对象的值。输出主要是文本形式。如果你希望以不同的方式显示某种类型的内容,无论是不同的文本输出、图像还是图表,你可以通过为特定类型注册自定义格式化器来实现。这些类型可以是你自己定义的,也可以是其他.NET库中定义的。\n",
"\n",
"一个常见的使用自定义格式化器的场景是渲染图表。一些NuGet包如Plotly.NET提供了.NET Interactive扩展利用此功能以交互式的HTML和JavaScript为基础生成输出。\n",
"\n",
"注册自定义格式化器最简单的方法是使用Formatter.Register<T>方法,它有几个不同的重载版本。在笔记本中使用最友好、最方便的一个接受两个参数:\n",
"+ 委托:它接受你要注册的类型的对象,并返回一个字符串。在这里,你可以指定所需的字符串转换;\n",
"+ MIME类型只有当使用此MIME类型时才会调用你的自定义格式化器\n",
"\n",
"下面的示例将 Rectangles 类的实例格式化为SVG矩形。"
]
},
{
"cell_type": "code",
"execution_count": 97,
"id": "494ec543",
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"public class Rectangle\n",
"{\n",
" public int Width {get;set;}\n",
" public int Height {get;set;}\n",
"}\n",
"\n",
"Microsoft.DotNet.Interactive.Formatting.Formatter.Register<Rectangle>\n",
"(\n",
" formatter: //格式化方法:把类型对象,转化为输出字符串\n",
" rect => $\"\"\"\n",
" <svg width=\"100\" height=\"100\">\n",
" <rect \n",
" width=\"{rect.Width}\" \n",
" height=\"{rect.Height}\" \n",
" style=\"fill:rgb(0,255,200)\" \n",
" />\n",
" </svg>\n",
" \"\"\", \n",
" mimeType: //输出媒体类型\n",
" \"text/html\"\n",
");"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<svg width=\"100\" height=\"100\">\r\n",
" <rect \r\n",
" width=\"100\" \r\n",
" height=\"50\" \r\n",
" style=\"fill:rgb(0,255,200)\" \r\n",
" />\r\n",
"</svg>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"/*\n",
"运行此代码后Rectangle对象将显示为图形矩形而不是属性值列表。\n",
"以下示例使用C#脚本尾随表达式语法该语法通常配置为在笔记本中使用text/html MIME类型。\n",
"*/\n",
"new Rectangle(){Width = 100, Height = 50}"
]
},
{
"cell_type": "markdown",
"id": "363d55e9",
"metadata": {},
"source": [
"特别指出:当在列表中遇到自定义类型或作为对象属性时,仍将调用自定义格式化程序"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><svg width=\"100\" height=\"100\">\r\n",
" <rect \r\n",
" width=\"200\" \r\n",
" height=\"50\" \r\n",
" style=\"fill:rgb(0,255,200)\" \r\n",
" />\r\n",
"</svg></td></tr><tr><td>1</td><td><svg width=\"100\" height=\"100\">\r\n",
" <rect \r\n",
" width=\"200\" \r\n",
" height=\"100\" \r\n",
" style=\"fill:rgb(0,255,200)\" \r\n",
" />\r\n",
"</svg></td></tr></tbody></table><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"new[] \n",
"{\n",
" new Rectangle(){Width = 200, Height = 50},\n",
" new Rectangle(){Width = 200, Height = 100}\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 100,
"id": "c6fd6734",
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ Name = Example, Rectangle = Submission#97+Rectangle }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>Example</pre></div></td></tr><tr><td>Rectangle</td><td><svg width=\"100\" height=\"100\">\r\n",
" <rect \r\n",
" width=\"100\" \r\n",
" height=\"50\" \r\n",
" style=\"fill:rgb(0,255,200)\" \r\n",
" />\r\n",
"</svg></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"//具有 Rectangle 类型属性的匿名对象\n",
"new {\n",
" Name = \"Example\",\n",
" Rectangle = new Rectangle(){ Width=100, Height=50 }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "39dab7b0",
"metadata": {},
"source": [
"### 打开泛型类型"
]
},
{
"cell_type": "markdown",
"id": "3606e605",
"metadata": {},
"source": [
"可以通过使用开放泛型类型定义作为键来指定格式化程序。以下代码将为所有类型的 T 的 List<T> 变体注册一个格式化程序,并打印每个元素及其哈希代码。(请注意,必须强制转换对象才能迭代其项。"
]
},
{
"cell_type": "code",
"execution_count": 101,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter.Register\n",
"(\n",
" type: typeof(List<>),\n",
" formatter: (list, writer) =>\n",
" {\n",
" foreach (var obj in (IEnumerable)list)\n",
" {\n",
" writer.WriteLine($\"{obj} ({obj.GetHashCode()})\");\n",
" }\n",
" }, \"text/html\"\n",
");"
]
},
{
"cell_type": "code",
"execution_count": 102,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"one (1556465726)\r\n",
"two (401392997)\r\n",
"three (-257294126)\r\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"//运行上述代码后,以下内容将不再仅打印列表中的值 [\"one\",\"two\",\"three\"]\n",
"//变为one (254814599) two (656421459) three (-1117028319)\n",
"var list = new List<string> { \"one\", \"two\", \"three\" };\n",
"list"
]
},
{
"cell_type": "markdown",
"id": "a8b71f19",
"metadata": {},
"source": [
"### TypeFormatterSource 特性类\n",
"Formatter.Register方式外另一种注册自定义格式化程序的方式是使用 TypeFormatterSourceAttribute 修饰类型。如果您想直接在笔记本中重新定义格式化程序设置,这不是最方便的方法。但是,如果您正在编写 .NET Interactive 扩展,或者编写包含某些类型的自定义格式的库或应用程序,则建议使用此方法。其中一个原因是基于属性的方法更简单。另一个原因是,调用 Formatter.ResetToDefault 时不会清除基于属性的格式化程序自定义,而使用 Formatter.Register 配置的格式化程序会被清除。您可以将基于属性的注册方法视为为类型设置默认格式的一种方式。\n",
"\n",
"基于属性的格式化程序注册有两种方法:一种用于项目引用时,另一种用于项目不引用 Microsoft.DotNet.Interactive.Formatting 时\n",
"\n",
"如果您已经引用了 Microsoft.DotNet.Interactive.Formatting ,例如,因为您正在编写 .NET Interactive 扩展,那么您可以使用 中定义的 TypeFormatterSourceAttribute 来修饰需要自定义格式的类型 Microsoft.DotNet.Interactive.Formatting 。下面是一个示例:"
]
},
{
"cell_type": "code",
"execution_count": 103,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"[Microsoft.DotNet.Interactive.Formatting.TypeFormatterSource(typeof(MyFormatterSource))]\n",
"public class MyTypeWithCustomFormatting\n",
"{\n",
"\n",
"}\n",
"\n",
"//带Mime类型\n",
"[Microsoft.DotNet.Interactive.Formatting.TypeFormatterSource\n",
"(\n",
" typeof(MyFormatterSource), PreferredMimeTypes = new[] { \"text/html\", \"application/json\" }\n",
")]\n",
"public class MyTypeWithCustomFormatting2\n",
"{\n",
"}\n",
"\n",
"//TypeFormatterSourceAttribute 指定的格式化程序源必须实现 ITypeFormatterSource并且必须具有空构造函数。它不需要是 public 类型\n",
"public class MyFormatterSource : Microsoft.DotNet.Interactive.Formatting.ITypeFormatterSource\n",
"{\n",
" public IEnumerable<Microsoft.DotNet.Interactive.Formatting.ITypeFormatter> CreateTypeFormatters()\n",
" {\n",
" return new Microsoft.DotNet.Interactive.Formatting.ITypeFormatter[]\n",
" {\n",
" new Microsoft.DotNet.Interactive.Formatting.PlainTextFormatter<MyTypeWithCustomFormatting>(context => \n",
" $\"Hello from {nameof(MyFormatterSource)} using MIME type text/plain\"),\n",
" new Microsoft.DotNet.Interactive.Formatting.HtmlFormatter<MyTypeWithCustomFormatting>(context => \n",
" $\"Hello from {nameof(MyFormatterSource)} using MIME type text/html\")\n",
" };\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "6437f82e",
"metadata": {},
"source": [
"一个完整例子:"
]
},
{
"cell_type": "code",
"execution_count": 104,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"[AttributeUsage(AttributeTargets.Class)]\n",
"internal class TypeFormatterSourceAttribute : Attribute\n",
"{\n",
" public TypeFormatterSourceAttribute(Type formatterSourceType)\n",
" {\n",
" FormatterSourceType = formatterSourceType;\n",
" }\n",
"\n",
" public Type FormatterSourceType { get; }\n",
"\n",
" public string[] PreferredMimeTypes { get; set; }\n",
"}\n",
"\n",
"internal class MyConventionBasedFormatterSource\n",
"{\n",
" public IEnumerable<object> CreateTypeFormatters()\n",
" {\n",
" yield return new MyConventionBasedFormatter { MimeType = \"text/html\" };\n",
" }\n",
"}\n",
"\n",
"internal class MyConventionBasedFormatter\n",
"{\n",
" public string MimeType { get; set; }\n",
"\n",
" public bool Format(object instance, System.IO.TextWriter writer)\n",
" {\n",
" if (instance is MyTypeWithCustomFormatting myObj)\n",
" {\n",
" writer.Write($\"<div>Custom formattering for {myObj}</div>\");\n",
" return true;\n",
" }\n",
" else\n",
" {\n",
" return false;\n",
" }\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "fbf04c96",
"metadata": {},
"source": [
"### 重置格式设置\n",
"在尝试不同的格式设置配置时,您可能会发现需要将所有内容重置为首次启动内核时看到的默认值。您可以轻松执行此作:"
]
},
{
"cell_type": "code",
"execution_count": 105,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"Microsoft.DotNet.Interactive.Formatting.Formatter.ResetToDefault();"
]
},
{
"cell_type": "markdown",
"id": "575b3a6a",
"metadata": {},
"source": [
"### 如何选择格式化程序\n",
"可以注册多个可能适用于同一类型的格式化程序。例如,可以为 object、IEnumerable<string> 和 IList<string> 注册格式化程序,其中任何一个都可能合理地应用于 List<string> 的实例。由于这些原因,了解如何选择 formatter 可能很有用。"
]
},
{
"cell_type": "markdown",
"id": "d4aaa243",
"metadata": {},
"source": [
"为 A 类型的对象选择适用的格式化程序,如下所示:\n",
"+ 选择 MIME 类型:\n",
" + 选择与 A 相关的最具体的用户注册 MIME 类型首选项\n",
" + 如果没有相关的用户注册的 MIME 类型,则使用配置的默认 MIME 类型\n",
"+ 选择一个格式化程序:\n",
" + 选择与 A 相关的最具体的用户注册格式化程序\n",
" + 如果没有相关的用户注册格式化程序,则选择默认格式化程序\n",
"\n",
"> 在这里,“最具体”是指类和接口层次结构。如果顺序完全一致或存在其他冲突,则首选较新的注册。当泛型类型的类型定义相同时,泛型类型的类型实例化优先于泛型格式化程序。\n",
"> MIME 类型的默认格式化程序集始终包括 object 的格式化程序"
]
},
{
"cell_type": "code",
"execution_count": 107,
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#97+Rectangle</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Width</td><td><div class=\"dni-plaintext\"><pre>100</pre></div></td></tr><tr><td>Height</td><td><div class=\"dni-plaintext\"><pre>50</pre></div></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>\r\n"
]
}
],
"source": [
"using System.IO;\n",
"using Microsoft.DotNet.Interactive.Formatting;\n",
"\n",
"ITypeFormatter formatter = Formatter.GetPreferredFormatterFor( typeof(Rectangle), Formatter.DefaultMimeType);\n",
"\n",
"var rect = new Rectangle { Width = 100, Height = 50 };\n",
"\n",
"var writer = new StringWriter();\n",
"\n",
"formatter.Format(rect, writer);\n",
"\n",
"Console.WriteLine(writer.ToString());"
]
},
{
"cell_type": "markdown",
"id": "6cbb3edb",
"metadata": {},
"source": [
"Examples 例子, 用于说明 Formatter 选择的工作原理\n",
"\n",
"+ 如果您为类型 A 注册格式化程序,则该格式化程序将用于类型 A 的所有对象(直到稍后指定类型 A 的替代格式化程序)\n",
"+ 如果为 System.Object 注册格式化程序,则它优先于所有其他格式化程序,但其他更具体的用户定义的格式化程序除外\n",
"+ 如果为任何 sealed 类型注册格式化程序,则它优先于所有其他格式化程序(除非为该类型指定了更多格式化程序)\n",
"+ 如果注册 List<> 和 List<int> 格式化程序,则 List<int> 格式化程序优先用于 List<int> 类型的对象,而 List<> 格式化程序优先用于其他泛型实例化,例如 List<string>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"languageName": "csharp",
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}