必备 .NET - C# 脚本
作者:Mark Michaelis | 2016 年 1 月
Link: https://msdn.microsoft.com/zh-cn/magazine/mt614271.aspx
C:\Program Files (x86)\Microsoft Visual Studio 14.0>csi
Microsoft (R) Visual C# Interactive Compiler version 1.1.0.51014
Copyright (C) Microsoft Corporation. All rights reserved.
Type "#help" for more information.
> System.Console.WriteLine("Hello! My name is Inigo Montoya");
Hello! My name is Inigo Montoya
> ConsoleColor originalConsoleColor = Console.ForegroundColor;
. Console.ForegroundColor = ConsoleColor.Red;
. Console.WriteLine("You killed my father. Prepare to die.");
. Console.ForegroundColor = originalConsoleColor;
You killed my father. Prepare to die.
> IEnumerable<Process> processes = Process.GetProcesses();
> using System.Collections.Generic;
> processes.Where(process => process.ProcessName.StartsWith("c") ).
. Select(process => process.ProcessName ).Distinct()
DistinctIterator { "chrome", "csi", "cmd", "conhost", "csrss" }
> processes.First(process => process.ProcessName == "csi" ).MainModule.FileName
"C:\\Program Files (x86)\\MSBuild\\14.0\\bin\\csi.exe"
> $"The current directory is { Environment.CurrentDirectory }."
"The current directory is C:\\Program Files (x86)\\Microsoft Visual Studio 14.0."
尽管 C# 脚本支持的远不止语句和表达式。您可以声明自定义类型、通过属性嵌入类型元数据,甚至可以使用特定于 C# 脚本的陈述句简化赘言。考虑图 2 中的拼写检查示例。
#r ".\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll"
#load "Mashape.csx" // Sets a value for the string Mashape.Key
using System.Collections.Generic;
public string Original { get; set; }
public string Suggestion { get; set; }
[JsonProperty(PropertyName ="corrections")]
private JObject InternalCorrections { get; set; }
public IEnumerable<string> Corrections
return InternalCorrections?[Original].Select(
x => x.ToString()) ?? Enumerable.Empty<string>();
else return Enumerable.Empty<string>();
get { return Original == Suggestion; }
static public bool Check(string word, out IEnumerable<string> corrections)
Task <Spell> taskCorrections = CheckAsync(word);
corrections = taskCorrections.Result.Corrections;
return taskCorrections.Result.IsCorrect;
static public async Task<Spell> CheckAsync(string word)
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
$"https://montanaflynn-spellcheck.p.mashape.com/check/?text={ word }");
request.ContentType = "application/json";
request.Headers = new WebHeaderCollection();
// Mashape.Key is the string key available for
// Mashape for the montaflynn API.
request.Headers.Add("X-Mashape-Key", Mashape.Key);
using (HttpWebResponse response =
await request.GetResponseAsync() as HttpWebResponse)
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
using(Stream stream = response.GetResponseStream())
using(StreamReader streamReader = new StreamReader(stream))
string strsb = await streamReader.ReadToEndAsync();
Spell spell = Newtonsoft.Json.JsonConvert.DeserializeObject<Spell>(strsb);
// Assume spelling was only requested on first word.
C# 脚本中注释的其他构造包括属性使用、使用语句、属性和函数声明,以及支持 async/await。鉴于后者支持,它甚至可以在 REPL 窗口中利用 await:
(await Spell.CheckAsync("entrepreneur")).IsCorrect
Enter If the current submission appears to be complete, evaluate it.
Escape Clear the current submission.
UpArrow Replace the current submission with a previous submission.
DownArrow Replace the current submission with a subsequent
submission (after having previously navigated backward).
#help Display help on available commands and key bindings.
Microsoft (R) Visual C# Interactive Compiler version 1.1.0.51014
Copyright (C) Microsoft Corporation. All rights reserved.
Usage: csi [option] ... [script-file.csx] [script-argument] ...
/help Display this usage message (alternative form: /?)
/i Drop to REPL after executing the specified script
/r:<file> Reference metadata from the specified assembly file
(alternative form: /reference)
/r:<file list> Reference metadata from the specified assembly files
(alternative form: /reference)
/lib:<path list> List of directories where to look for libraries specified
by #r directive (alternative forms: /libPath /libPaths)
/u:<namespace> Define global namespace using
(alternative forms: /using, /usings, /import, /imports)
@<file> Read response file for more options
-- Indicates that the remaining arguments should not be
正如前面所说,csi.exe 允许您指定可自定义命令窗口的默认"profile"文件。
图 5 使用 Visual Studio C# 交互窗口在类外声明 C# 脚本函数
输入 | 如果当前提交似乎完整,则对其进行评估。否则,插入新行。 |
Ctrl+Enter | 在当前提交内,评估当前提交。 |
Shift+Enter | 插入新行。 |
Escape | 清除当前提交。 |
Alt+UpArrow | 将当前提交替换为上一提交。 |
Alt+DownArrow | 将当前提交替换为随后的提交(之前执行了向后导航后)。 |
Ctrl+Alt+UpArrow | 将当前提交替换为以相同文本开始的上一提交。 |
Ctrl+Alt+DownArrow | 将当前提交替换为以相同文本开始的随后的提交(之前执行了向后导航后)。 |
UpArrow | 在当前提交末尾,将当前提交替换为上一提交。 在其他位置,将游标上移一行。 |
DownArrow | 在当前提交末尾,将当前提交替换为随后的提交(之前执行了向后导航后)。 在其他位置,将游标下移一行。 |
Ctrl+K、Ctrl+Enter | 在交互缓冲区末尾粘贴选择,在输入末尾保留插入点。 |
Ctrl+E、Ctrl+Enter | 在交互缓冲区中任何挂起输入之前,粘贴并执行选择。 |
Ctrl+A | 第一次按下,选择包含游标的提交。第二次按下,选择窗口中的所有文本。 |
# This file contains command-line options that the C# REPL
# will process as part of every compilation, unless
# "/noconfig" option is specified in the reset command.
/r:System.Data.DataSetExtensions
using System.Collections.Generic;
请注意,您可以反复声明相同的构造(变量、类、函数等)。最新声明会覆盖早期声明。
我承认我对 Windows PowerShell 爱恨交加。我喜欢命令行上具有 Microsoft .NET Framework 的方便以及可以在管道中传递 .NET 对象,但我不喜欢之前 CLI 中的许多传统文本。即便如此,当涉及到 C# 语言时,我由衷地喜爱它的简洁和强大功能。(至今,我仍对使 LINQ 成为现实的语言扩展印象深刻。) 因此,我应将 Windows PowerShell .NET 的广度与 C# 语言的简洁相结合的想法,意味着我将 C# REPL 作为 Windows PowerShell 的替代品。启动 csi.exe 后,我立即尝试了 cd、dir、ls、pwd、cls、alias 等命令。一言以蔽之,我非常失望,因为这些命令均无法使用。思考体验并与 C# 团队对其进行讨论后,我意识到对于版本 1,团队关注的重点不是替换 Windows PowerShell,而是关注 .NET Framework,因此通过为前面的命令添加您自己的函数,甚至可通过在 Roslyn 上更新 C# 脚本实施,均可支持扩展性。我立即着手为这些命令定义函数。此库的入门教程可从 GitHub 下载:github.com/CSScriptEx。
对于想要寻找功能更强大的 C# CLI(可支持现已可用的先前命令列表)的用户,请考虑 scriptcs.net 上的 ScriptCS(也可从 github.com/scriptcs 的 GitHub 上获得)。它也利用 Roslyn 并包含 alias、cd、clear、cwd、exit、help、install、references、reset、scriptpacks、usings 和 vars。请注意,通过 ScriptCS,命令前缀现在是冒号(如 :reset)而非数字记号(如 #reset)。作为额外奖励,ScriptCS 也以着色和 IntelliSense 形式为 Visual Studio Code 添加了 CSX 文件支持。
总结
至少现在,C# REPL 界面的目的不是替换 Windows PowerShell 甚或 cmd.exe。要想在开始时实现此目的,将会导致失望。当然,我建议您尽可能使 C# 脚本和 REPL CLI 实现 Visual Studio | 新项目的轻型替换: UnitTestProject105 或目的相似的 dotnetfiddle.net。这些是面向 C# 和 .NET 的方法,用于加强您对语言和 .NET API 的理解。C# REPL 提供了对短代码段或程序单元进行编码的方式,您可以即兴使用,直到它们已被剪切并粘贴到大型程序中。它允许您在写入代码时写入语法已验证的更广泛脚本(即便存在大小写不匹配这样的小问题),而不会强制您仅执行脚本以发现输入错误的内容。一旦您知道它的位置,C# 脚本及其交互窗口会变得乐趣无穷,这正是自版本 1.0 起您一直期待的工具。
如同 C# REPL 和 C# 脚本自身一样有趣,认为它们也为成为您自己应用程序的扩展框架提供了跳板—仿照 Visual Basic for Applications (VBA)。通过交互窗口和 C# 脚本支持,您可以想像一个世界—不是太遥远—在这个世界里,您可以将 .NET"宏"再次添加到自己的应用程序中,而无需创建自定义语言、分析器和编辑器。现在,传统 COM 功能正是时候值得引领我们走向新世界。
Mark Michaelis 是 IntelliTect 的创始人,担任首席技术架构师和培训师。在近二十年的时间里,他一直是 Microsoft MVP,并且自 2007 年以来一直担任 Microsoft 区域总监。Michaelis 还是多个 Microsoft 软件设计评审团队(包括 C#、Microsoft Azure、SharePoint 和 Visual Studio ALM)的成员。他在开发者会议上发表了演讲,并撰写了大量书籍,包括最新的"必备 C# 6.0(第 5 版)"(itl.tc/EssentialCSharp)。可通过他的 Facebook facebook.com/Mark.Michaelis、博客 IntelliTect.com/Mark、Twitter @markmichaelis 或电子邮件 mark@IntelliTect.com 与他取得联系。
衷心感谢以下 Microsoft 技术专家对本文的审阅: Kevin Bost 和 Kasey Uhlenhuth