{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "在笔记中共享数据(变量)\n", "====================" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用 .NET 交互式内核,可以在单个笔记本中以多种语言编写代码。为了利用每种语言的不同优势,您会发现在它们之间共享数据很有用。即一种语言的变量,可以在其它语言中使用。\n", "默认情况下,.NET Interactive 支持多种不同的语言,其中大多数语言都允许使用magic 命令`#!set` 和 `#!shared` 进行共享。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 变量共享的语言支持情况" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| 语言 | 变量共享 |\n", "| ---- | ---- |\n", "| C# | 支持 |\n", "| F# | 支持 |\n", "| PowerShell | 支持 |\n", "| JavaScript | 支持 |\n", "| SQL | 支持 |\n", "| KQL | 支持 |\n", "| Python | 支持 |\n", "| R | 支持 |\n", "| HTML | 不支持 |\n", "| Mermaid | 不支持 |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 同种内核 默认共享数据" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同种内核的不同单元格之间,无需任何操作,变量默认共享,后续单元格直接使用前面已执行单元格的数据。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ JS 各单元格共享示例:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "polyglot_notebook": { "kernelName": "javascript" } }, "outputs": [], "source": [ "//声明变量\n", "JsShared = \"jsShared\";" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "polyglot_notebook": { "kernelName": "javascript" } }, "outputs": [ { "data": { "text/plain": [ "jsShared" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "//直接使用上面单元格变量\n", "console.log(JsShared);" ] }, { "cell_type": "markdown", "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "source": [ "+ C# 各单元格共享示例:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "//声明变量\n", "string CsharpShared = \"CsharpShared\";" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CsharpShared\r\n" ] } ], "source": [ "//直接使用\n", "Console.WriteLine(CsharpShared);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 使用 `#!set` 和 `#!shared` 魔法命令共享数据" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> `#!shared` 魔法命令从 .NET Interactive 的早期就已经存在,而 `#!set` 是较新的命令,它提供了` #!share` 功能的超集。由于 `#!set` 具有更丰富的功能并且更具可读性,因此就优先使用`#!set`。\n", "> 把 `#!share` 命令重写为 `#!set` 命令很容易" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`#!share` 用法的示例:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "polyglot_notebook": { "kernelName": "javascript" } }, "outputs": [], "source": [ "//声明一个要被共享的js变量\n", "\n", "//共享的变量声明不要加var、let、const关键字,加了变局部变量\n", "jsVar = \"js变量值\";\n", "\n", "\n", "#!share --from javascript jsVar --as csVarFromJs22222\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "js变量值\r\n" ] } ], "source": [ "//共享变量\n", "\n", "#!share --from javascript jsVar --as csVarFromJs\n", "Console.WriteLine(csVarFromJs);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "改写为 等价的 `#!set` 命令:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "js变量值\r\n" ] } ], "source": [ "#!set --name csVarFromJs --value @javascript:jsVar\n", "Console.WriteLine(csVarFromJs);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`#!share`命令通过 --from 选项,声明了共享数据来源,通过 --as 选项 声明共享变量的新名称,方便后续使用;\n", "`#!set`命令通过更加明确的选项 `--name` 和 `--value` 选项, 指明了共享数据的值(形如:@来源:值形)和新名称;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 变量视图:管理变量" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![变量共享](./assets/images/shared.001.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 内核之间共享数据" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "示例:C# 运行中的变量,被其它语言共享。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "//定义变量:存储网关\n", "string getway = \"192.168.1.1\";" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ PowerShell 中使用" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "dotnet_interactive": { "language": "pwsh" }, "polyglot_notebook": { "kernelName": "pwsh" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "192.168.1.1\r\n" ] } ], "source": [ "# Poweshell中使用 前面C#单元中定义的变量\n", "# 特别注意:因为PS中变量名必须以$开头,所以在命令中 name 参数名,在PS中使用时必须加$前辍\n", "\n", "#!set --value @csharp:getway --name gw\n", "\n", "Write-Host $gw" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ F# 中使用" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "dotnet_interactive": { "language": "fsharp" }, "polyglot_notebook": { "kernelName": "fsharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "192.168.1.1\r\n" ] } ], "source": [ "#!set --value @csharp:getway --name getway\n", "\n", "Console.WriteLine(getway)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ 在javascrip中使用" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "dotnet_interactive": { "language": "javascript" }, "polyglot_notebook": { "kernelName": "javascript" } }, "outputs": [ { "data": { "text/plain": [ "192.168.1.1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!set --value @csharp:getway --name getway\n", "console.log(getway);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 从用户输入中设置变量" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "共享数据变量的值,不但能直接设置、来自其它变量,还可以是由用户输入的。这在需要用户交互时,非常有用,比如:需要用户输入密码、流程控制由用户选择等。\n", "\n", "注意:执行后,会在VS Code顶部,弹出一个小的用户输入窗口,用户输入内容并且确认后,用户的输入内容会被存储为变量的值." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "可以在魔法命令中使用一个@input前缀,直接从用户输入中设置一个值。比如:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入的姓名是:小明\r\n" ] } ], "source": [ "#!set --name userName --value @input(\"请输入姓名\");\n", "Console.WriteLine($\"输入的姓名是:{userName}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果希望用户输入在UI中被遮盖(比如不希望在屏幕上显示的秘密),可以使用@password前缀来代替@input" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入的密码是:************\r\n" ] } ], "source": [ "#!set --name userPassword --value @password(\"请输入密码\");\n", "Console.WriteLine($\"输入的密码是:{userPassword}\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通过和前缀请求用户输入的能力不仅仅局限于共享数据的魔法命令,还可以在程序中使用。比如:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入为:小小\r\n" ] } ], "source": [ "using Microsoft.DotNet.Interactive;\n", "\n", "var input = await Kernel.GetInputAsync(\"Pick a number.\");\n", "Console.WriteLine($\"输入为:{input}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## MIME 类型" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在.NET Interactive中,当变量在子内核之间共享时,通常需要将其转换为某种字符串表示形式。这是因为.NET Interactive中的许多子内核运行在不同的进程中。例如,核心内核在其自己的.NET进程中运行,而多语言笔记本扩展在VS Code进程中运行。你还可以在远程机器上运行子内核。子内核也可以在不同的平台上实现,例如.NET和JavaScript。\n", "\n", "因此,虽然在共享进程时,.NET语言之间可以通过引用共享变量,但共享的主要用例涉及某种形式的序列化。序列化格式由MIME类型指定,用户可以通过可选的选项来指定。如果不指定选项,则默认使用text/plain MIME类型用于变量共享。\n", "\n", "这意味着请求的变量将由源内核序列化为JSON,然后可选地由目标内核进行反序列化。对于基于.NET的内核,序列化使用特定的方法进行。在基于.NET的目标内核中使用的反序列化策略如下:\n", "\n", "| 源json类型 | 目标.NET类型 |\n", "| ---- | ---- |\n", "| boolean | System.Boolean |\n", "| number | System.Double |\n", "| string | System.String |\n", "| other | System.Text.Json.JsonDocument |\n", "\n", "将变量转换为指定的MIME类型是通过使用.NET Interactive格式化API完成的,这些API可以定制。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 引用共享" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "默认情况下的共享是值共享(即副本共享),在特定情况下,引用类型变量可以通过引用进行共享。但要注意:\n", "+ 源和目标内核必须在同一进程中运行。\n", "+ 源和目标内核必须基于公共语言运行时(如C#、F#、PowerShell)。\n", "+ 如果使用的是#!set--byref,引用共享仅在使用该选项时启用。\n", "+ 如果使用的是#!share--mime-type,引用共享是默认行为,但在使用该选项时会禁用。\n", "\n", "因此,如果共享一个可变对象,其状态的更改将在子内核间立即可见,这与默认的基于序列化的共享不同。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "polyglot_notebook": { "kernelName": "fsharp" } }, "outputs": [], "source": [ "//F# 声明数组\n", "open System.Collections.Generic;\n", "let messages = List()\n", "messages.Add \"由F#添加\"" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
[ 由F#添加, 由C#添加 ]
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "//C#获取并修改\n", "#!set --byref --value @fsharp:messages --name msgList\n", "\n", "msgList.Add(\"由C#添加\");\n", "msgList.Display();\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "polyglot_notebook": { "kernelName": "fsharp" } }, "outputs": [ { "data": { "text/html": [ "
[ 由F#添加, 由C#添加 ]
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "//F#中的原数组,已被C#修改\n", "messages" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `#!value` 内核,直接设置值" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在笔记本中使用文本是很常见的需求。这些文本可能是JSON、CSV、XML或其他格式。它们可能存在于文件中、剪贴板上,或者在网页上。\n", "\n", "为了尽可能方便地将这些文本导入到笔记本中的变量里,我们提供了`#!value`魔法命令。需要知道的重要一点是,这是一个别名,指向一个专门设计用于存储值的子内核。这意味着一旦将某些内容存储在其中,就可以通过或从另一个子内核访问它(`#!set` `#!share`)\n", "\n", "有三种方法可以使用来将数据导入到你的笔记本会话中:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ 剪贴板\n", "\n", "最简单的使用方法是将一些文本粘贴到单元格中。文本将被存储为字符串,但与在C#、F#或PowerShell中使用字面量不同,这里不需要转义任何内容。" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "polyglot_notebook": { "kernelName": "value" } }, "outputs": [ { "data": { "text/plain": [ "{\r\n", " \"Id\":2,\r\n", " \"Name\":\"小李\",\r\n", " \"Age\":33\r\n", "}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!value --name StudentJson\n", "{\n", " \"Id\":2,\n", " \"Name\":\"小李\",\n", " \"Age\":33\n", "}" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/plain": [ "{\r\n", " \"Id\":2,\r\n", " \"Name\":\"小李\",\r\n", " \"Age\":33\r\n", "}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!set --name fromValueKernel --value @value:StudentJson\n", "\n", "fromValueKernel.Display();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ 文件" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "数据存储在一个文件中时,使用带有选项的命令:#!value--from-file, 获取共享数据。" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "polyglot_notebook": { "kernelName": "value" } }, "outputs": [ { "data": { "text/plain": [ "{\r\n", " \"Id\":2,\r\n", " \"Name\":\"小李\",\r\n", " \"Age\":33\r\n", "}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!value --name fromFileData --from-file ./shared/file/data.json" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/plain": [ "{\r\n", " \"Id\":2,\r\n", " \"Name\":\"小李\",\r\n", " \"Age\":33\r\n", "}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!set --name fromValueFileData --value @value:fromFileData\n", "\n", "fromValueFileData.Display();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ URL" ] }, { "cell_type": "markdown", "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "source": [ "也可以使用--from-url选项,从一个URL地址获取数据" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "polyglot_notebook": { "kernelName": "value" } }, "outputs": [ { "data": { "application/json": { "code": 403 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!value --name fromUrlData --from-url https://www.qq.com" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
{\n", " "code":403\n", "}
code
403
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!set --name fromValueUrlData --value @value:fromUrlData\n", "\n", "fromValueUrlData.Display();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 指定 MIME 类型" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "无论使用哪种方法,都可以使用`--mime-type`选项在提交时选择在笔记本中显示值。如果笔记本前端知道如何显示mime类型,可以看到它格式正确:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "polyglot_notebook": { "kernelName": "value" } }, "outputs": [ { "data": { "application/json": { "Adress": { "Code": "0394", "info": "变法路36号101" }, "Id": 2, "Name": "小张" } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!value --name JsonData --mime-type application/json\n", "{\n", " \"Id\":2,\n", " \"Name\":\"小张\",\n", " \"Adress\":{\n", " \"Code\":\"0394\",\n", " \"info\":\"变法路36号101\"\n", " }\n", "}" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "polyglot-notebook" }, "orig_nbformat": 4, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "name": "csharp" }, { "aliases": [], "name": "razor" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }