Janeiro de 2016
Volume 31 – Número 1
Essential .NET - Script do C#
Por Mark Michaelis | janeiro de 2016
Com a chegada do Visual Studio 2015 Update 1, mencionado de agora em diante como Update 1, há um novo REPL (ler-avaliar-imprimir-executar um loop) do C#, disponível como uma nova janela interativa no Visual Studio 2015 ou como uma nova CLI (interface de linha de comando) chamada CSI. Além de trazer a linguagem C# para a linha de comando, o Update 1 também introduz uma nova linguagem de script do C#, tradicionalmente salva em um arquivo CSX.
Antes de nos aprofundarmos nos detalhes do novo script do C#, é importante entender os cenários de destino. O script do C# é uma ferramenta para testar trechos de C# e .NET sem a necessidade de criar vários projetos de console ou de teste de unidades. Ele fornece uma opção leve para codificar rapidamente uma chamada de método agregado LINQ na linha de comando, verificando a API .NET para descompactação de arquivos ou invocando uma API REST para descobrir o que ela retorna ou como ela funciona. Ele fornece um meio fácil de explorar e compreender uma API sem a sobrecarga de mais um arquivo CSPROJ no diretório %TEMP%.
A interface de linha de comando REPL do C# (CSI.EXE)
Assim como ocorre com o aprendizado do próprio C#, a melhor maneira de começar a aprender a interface do REPL do C# é executá-la e começar a executar comandos. Para iniciá-la, execute o comando csi.exe no prompt de comando de desenvolvedor do Visual Studio 2015 ou use o caminho completo, C:\Program Files (x86)\MSBuild\14.0\bin\csi.exe. Comece então a executar instruções em C#, como as mostradas na Figura 1.
Figura 1 Exemplo de REPL da CSI
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;
> try{
. Console.ForegroundColor = ConsoleColor.Red;
. Console.WriteLine("You killed my father. Prepare to die.");
. }
. finally
. {
. 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."
>
O primeiro aspecto a ser observado é óbvio: ele é como o C#, embora seja um novo dialeto do C# (mas sem a complexidade que é apreciada em um programa de produção completo e é desnecessária em um protótipo rápido e rascunhado). Portanto, como seria de se esperar, se quiser chamar um método estático, você poderá escrever o nome do método totalmente qualificado e passar argumentos entre parênteses. Assim como em C#, você declara uma variável prefixando-a com o tipo e, opcionalmente, atribuindo-lhe um novo valor no momento da declaração. Novamente, como seria de se esperar, qualquer sintaxe de corpo de método válida (blocos try/catch/finally, declaração de variável, expressões lambda e LINQ) funciona perfeitamente.
Mesmo na linha de comando são mantidos outros recursos do C#, como constructos de cadeias de caracteres (diferenciação de maiúsculas e minúsculas, literais de cadeias de caracteres e interpolação de cadeias de caracteres). Portanto, quando você usa ou gera caminhos, as barras invertidas devem ser colocadas entre caracteres de escape usando-se um caractere de escape do C# ("\") ou um literal de cadeia de caracteres, e a mesma coisa vale para as barras invertidas duplas na saída de um caminho como o de csi.exe. A interpolação de cadeia de caracteres também funciona, como demonstra a linha de exemplo de "diretório atual" na Figura 1.
No entanto, os scripts do C# possibilitam muito mais do que instruções e expressões. Você pode declarar tipos personalizados, inserir metadados de tipo por meio de atributos e até mesmo simplificar o nível de detalhes usando declarativos específicos de script do C#. Considere o exemplo de verificação ortográfica na Figura 2.
Figura 2 A classe de script do C# Spell (Spell.csx)
#r ".\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll"
#load "Mashape.csx" // Sets a value for the string Mashape.Key
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Spell
{
[JsonProperty("original")]
public string Original { get; set; }
[JsonProperty("suggestion")]
public string Suggestion { get; set; }
[JsonProperty(PropertyName ="corrections")]
private JObject InternalCorrections { get; set; }
public IEnumerable<string> Corrections
{
get
{
if (!IsCorrect)
{
return InternalCorrections?[Original].Select(
x => x.ToString()) ?? Enumerable.Empty<string>();
}
else return Enumerable.Empty<string>();
}
}
public bool IsCorrect
{
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.Method = "POST";
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}).",
response.StatusCode,
response.StatusDescription));
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.
return spell;
}
}
}
}
Na maioria das vezes, essa é apenas uma declaração de classe C# padrão. No entanto, há vários recursos de scripts do C# específicos. Em primeiro lugar, a diretiva #r serve para referenciar um assembly externo. Nesse caso, a referência é à Newtonsoft.Json.dll, que ajuda a analisar os dados JSON. No entanto, observe que essa é uma diretiva projetada para referenciar arquivos no sistema de arquivos. Assim, ela não requer a complexidade desnecessária de uma sequência de escape de barra invertida.
Em segundo lugar, você pode salvar a lista inteira como um arquivo CSX e depois "importar" ou "embutir" o arquivo na janela do REPL do C# usando #load Spell.csx. A diretiva #load permite incluir arquivos de script adicionais, como se todos os arquivos #load fossem incluídos no mesmo "projeto" ou "compilação". Colocar o código em um arquivo de script do C# separado habilita um tipo de refatoração de arquivo e, o que é mais importante, a capacidade de persistir o script do C# ao longo do tempo.
As declarações "using" constituem outro recurso da linguagem C# permitido nos scripts do C#, que a Figura 2 aproveita várias vezes. Observe que, assim como em C#, uma declaração "using" tem o arquivo como escopo. Portanto, se você chamar #load Spell.csx a partir da janela do REPL, o declarativo "using Newtonsoft.Json" não persistirá fora de Spell.csx. Em outras palavras, "using Newtonsoft.Json" dentro de Spell.csx não persistirá na janela do REPL sem ser declarado explicitamente de novo na janela do REPL (e vice-versa). Observe que o declarativo "using static" do C# 6.0 também tem suporte. Portanto, um declarativo " using static System.Console " elimina a necessidade de prefixar os membros de System.Console com o tipo, permitindo comandos REPL como "WriteLine("Hello! My name is Inigo Montoya").”
Outros constructos dignos de nota nos scripts do C# incluem o uso de atributos, as instruções "using", a declaração de propriedades e funções e o suporte a async/await. Graças ao suporte a esse último item, é possível até tirar proveito de await na janela do REPL:
(await Spell.CheckAsync("entrepreneur")).IsCorrect
Eis aqui mais algumas observações sobre a interface do REPL do C#:
- Você não pode executar o csi.exe dentro do ISE (Ambiente de Script Integrado) do Windows PowerShell, pois ele requer a entrada no console direta, que não tem suporte nas janelas do console "simuladas" do ISE do Windows PowerShell. (Por esse motivo, considere adicionar à lista sem suporte de aplicativos de console —$psUnsupportedConsoleApplications.)
- Não há um comando "sair" ou "encerrar" para sair do programa CSI. Em vez disso, você usa Ctrl+C para encerrar o programa.
- O histórico de comandos persiste entre as sessões do csi.exe iniciadas a partir da mesma sessão do cmd.exe ou do PowerShell.exe. Por exemplo, se você iniciar o csi.exe, invocar Console.WriteLine ("HelloWorld"), usar Ctrl+C para sair e reiniciar o csi.exe, a seta para cima exibirá o comando anterior de Console.WriteLine("HelloWorld"). Se você sair da janela do cmd.exe e o reiniciar, ele limpará o histórico.
- O csi.exe dá suporte ao comando REPL #help, que exibe a saída mostrada na Figura 3.
- O csi.exe dá suporte a várias opções de linha de comando, como mostrado na Figura 4.
Figura 3 A saída do comando REPL #help
> #help
Keyboard shortcuts:
Enter If the current submission appears to be complete, evaluate it.
Otherwise, insert a new line.
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).
REPL commands:
#help Display help on available commands and key bindings.
Figura 4 Opções de linha de comando do csi.exe
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] ...
Executes script-file.csx if specified, otherwise launches an interactive REPL (Read Eval Print Loop).
Options:
/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
treated as options
Conforme mencionado, o csi.exe permite especificar um arquivo de "perfil" padrão que personaliza a janela de comando:
- Para limpar o console CSI, invoque Console.Clear. (Considere o uso de um declarativo "using static System.Console " para adicionar suporte para simplesmente invocar Clear.)
- Se estiver inserindo um comando de várias linhas e cometer um erro em uma linha anterior, você poderá usar Ctrl+Z seguido de Enter para cancelá-lo e retornar a um prompt de comando vazio sem o executar (observe que ^Z será mostrado no console).
A janela interativa do Visual Studio C#
Como mencionei, também há uma nova janela interativa do Visual Studio C# no Update 1, como mostrado na Figura 5. A janela interativa do C# é iniciada por meio do menu Exibir | Outras Janelas | C# Interativo, que abre uma janela encaixada adicional. Assim como a janela do csi.exe, trata-se de uma janela do REPL do C#, mas com alguns recursos adicionais. Primeiramente, ela inclui codificação de cores de sintaxe e IntelliSense. Da mesma forma, a compilação ocorre em tempo real, à medida que você edita. Assim, erros de sintaxe e outros erros são automaticamente sublinhados com uma linha sinuosa vermelha.
Figura 5 Como declarar uma função de script do C# fora de uma classe usando a Janela Interativa do Visual Studio C#
Evidentemente, uma associação comum com a Janela Interativa do C# são as janelas Imediata e de Comando do Visual Studio. Embora existam sobreposições (afinal, ambas são janelas REPL em relação às quais você pode executar instruções .NET), elas têm finalidades significativamente diferentes. A Janela Imediata do C# está diretamente associada ao contexto de depuração do aplicativo, permitindo injetar instruções adicionais no contexto, examinar dados dentro da sessão de depuração e até mesmo manipular e atualizar o contexto de dados e depuração. De forma semelhante, a Janela de Comando fornece uma CLI para manipular o Visual Studio, incluindo a execução dos vários menus, mas na Janela de Comando, em vez de nos próprios menus. (A execução do comando View.C#Interactive, por exemplo, abre a Janela Interativa do C#.) Por outro lado, a Janela Interativa do C# permite executar o C#, inclusive todos os recursos relativos à interface REPL do C# discutidos na seção anterior. No entanto, a Janela Interativa do C# não tem acesso ao contexto de depuração. Trata-se de uma sessão do C# totalmente independente, sem identificadores para o contexto de depuração ou mesmo para o Visual Studio. Assim como o csi.exe, trata-se de um ambiente que permite experimentar trechos rápidos de C# e .NET para verificar sua compreensão sem ter que iniciar mais um console do Visual Studio ou um projeto de teste de unidade. No entanto, em vez de ter que iniciar um programa separado, a Janela Interativa do C# está hospedada no Visual Studio, em que, presumivelmente, o desenvolvedor já reside.
Eis aqui algumas observações sobre a Janela Interativa do C#:
- A Janela Interativa do C# dá suporte a vários comandos REPL adicionais não encontrados no csi.exe, incluindo:
- #cls/#clear para limpar o conteúdo da janela do editor
- #reset para restaurar o ambiente de execução ao seu estado inicial, mantendo o histórico de comandos
- Os atalhos de teclado são um pouco inesperados, como mostra a saída de #help na Figura 6.
Figura 6 Atalhos de teclado para a Janela Interativa do C#
Inserir | Se o envio atual parecer completo, avaliá-lo. Caso contrário, inserir uma nova linha. |
Ctrl+Enter | No envio atual, avaliar o envio atual. |
Shift+Enter | Inserir uma nova linha. |
Escape | Limpar o envio atual. |
Alt+Seta para cima | Substituir o envio atual por um envio anterior. |
Alt+Seta para baixo | Substituir o envio atual por um envio posterior (após ter navegado para trás anteriormente). |
Ctrl+Alt+Seta para cima | Substituir o envio atual por um envio anterior que comece com o mesmo texto. |
Ctrl+Alt+Seta para baixo | Substituir o envio atual por um envio subsequente que comece com o mesmo texto (após ter navegado para trás anteriormente). |
Seta para cima | No fim do envio atual, substituir o envio atual por um envio anterior. Em outros lugares, mover o cursor uma linha para cima. |
Seta para baixo | No fim do envio atual, substituir o envio atual por um envio posterior (após ter navegado para trás anteriormente). Em outros lugares, mover o cursor uma linha para baixo. |
Ctrl+K, Ctrl+Enter | Colar a seleção no fim do buffer interativo; manter o circunflexo no fim da entrada. |
Ctrl+E, Ctrl+Enter | Colar e executar a seleção antes de qualquer entrada pendente no buffer interativo. |
Ctrl+A | Primeiro pressionamento, selecionar o envio que contém o cursor. Segundo pressionamento, selecionar todo o texto contido na janela. |
É importante observar que Alt+Seta para cima/Seta para baixo são os atalhos de teclado para chamar novamente o histórico de comandos. A Microsoft os selecionou em vez dos atalhos simples Seta para cima/Seta para baixo porque queria que a experiência com a Janela Interativa correspondesse à de uma janela de código padrão do Visual Studio.
- Como a Janela Interativa do C# está hospedada no Visual Studio, não há uma oportunidade análoga de passar referências,declarativos "using" ou importações por meio da linha de comando, diferentemente do csi.exe. Em vez disso, a Janela Interativa do C# carrega seu contexto de execução padrão a partir de C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies\CSharpInteractive.rsp, que identifica os assemblies a serem referenciados por padrão:
# 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
/r:System.Core
/r:Microsoft.CSharp
/r:System.Data
/r:System.Data.DataSetExtensions
/r:System.Xml
/r:System.Xml.Linq
SeedUsings.csx
Além disso, o arquivo CSharpInteractive.rsp referencia um arquivo padrão C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies\SeedUsings.csx:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
A combinação desses dois arquivos é o motivo pelo qual você pode usar Console.WriteLine e Environment.CurrentDirectory em vez do System.Console.WriteLine e do System.Environ-ment.CurrentDirectory totalmente qualificados, respectivamente. Além disso, a referência a assemblies como Microsoft.CSharp permite o uso de recursos de linguagem como dynamic, sem mais nada. (A modificação desses arquivos é a maneira como você muda seu "perfil" ou suas "preferências" para que as alterações persistam entre sessões.)
Mais informações sobre a sintaxe de script do C#
Algo a ser lembrado sobre a sintaxe de script do C# é que grande parte da complexidade que é importante para o C# padrão torna-se adequadamente opcional em um script do C#. Por exemplo, itens como corpos de método não precisam aparecer dentro de uma função, e as funções de script do C# podem ser declaradas fora dos limites de uma classe. Você poderia definir uma função NuGet Install para aparecer diretamente na janela do REPL, por exemplo, como mostrado na Figura 5. Além disso, talvez surpreendentemente, os scripts do C# não são compatíveis com a declaração de namespaces. Por exemplo, você não pode inserir a classe Spell em um namespace Grammar: namespace Grammar { class Spell {} }.
É importante notar que você pode declarar o mesmo constructo (variável, classe, função e assim por diante) várias vezes. A última declaração se sobrepõe a qualquer declaração anterior.
Também é importante conhecer o comportamento do ponto-e-vírgula no fim de comandos. As instruções (atribuições de variáveis, por exemplo) exigem um ponto-e-vírgula. Sem o ponto-e-vírgula, a janela do REPL continuará a solicitar (por meio de um ponto) mais entradas até que o ponto-e-vírgula seja inserido. Por outro lado, as expressões são executadas sem ponto-e-vírgula. Assim, System.Diagnostics.Process.Start ("notepad") iniciará o Bloco de Notas mesmo sem o ponto-e-vírgula final. Além disso, como a chamada de método Start retorna um processo, a saída de cadeia de caracteres da expressão aparecerá na linha de comando: [System.Diagnostics.Process (Notepad)]. No entanto, se você fechar uma expressão com um ponto-e-vírgula, ocultará a saída. Portanto, a invocação de Start com um ponto-e-vírgula final não produz saída, apesar de que o Bloco de Notas ainda assim será iniciado. Evidentemente, Console.WriteLine("Seria um milagre."); ainda assim gerará o texto, mesmo com o ponto-e-vírgula, pois o próprio método estará exibindo a saída (não o retorno do método).
Às vezes a distinção entre expressões e instruções pode resultar em diferenças sutis. Por exemplo, a cadeia de caracteres de instrução text ="There’s a shortage of perfect b…."; não resultará em saída, mas text="Stop that rhyming and I mean it" retornará a cadeia de caracteres atribuída (pois a atribuição retorna o valor atribuído, e não há um ponto-e-vírgula para suprimir a saída).
As diretivas de script do C# para referenciar assemblies adicionais (#r) e importar scripts do C# existentes (#load) são ótimas adições. (É possível imaginar soluções complexas como arquivos project.json para alcançar o mesmo resultado, mas elas não seriam tão elegantes.) Infelizmente, quando este artigo foi escrito, os pacotes NuGet ainda não tinham suporte. Para referenciar um arquivo do NuGet, você precisa instalar o pacote em um diretório e referenciar a DLL específica por meio da diretiva #r. (A Microsoft me garante que isso estará disponível em breve.)
Observe que, no momento, as diretivas se referem a arquivos específicos. Você não pode, por exemplo, especificar uma variável na diretiva. Embora isso pudesse ser esperado com uma diretiva, isso impede a possibilidade de se carregar dinamicamente um assembly. Por exemplo, você pode invocar dinamicamente "nuget.exe install" para extrair um assembly (veja novamente a Figura 5). No entanto, isso não permite que o arquivo CSX seja associado dinamicamente ao pacote NuGet extraído, pois não há uma maneira de passar dinamicamente o caminho do assembly para a diretiva #r.
Uma CLI do C#
Confesso que tenho uma relação de amor e ódio com o Windows PowerShell. Adoro a praticidade de ter o Microsoft .NET Framework na linha de comando e a possibilidade de passar objetos .NET no pipe, em vez do texto tradicional de muitas das CLIs anteriores. Dito isso, sou partidário da linguagem C#: adoro sua elegância e sua capacidade. (Até hoje, ainda fico impressionado com as extensões da linguagem que possibilitaram o LINQ.) Portanto, a possibilidade de ter a amplitude do Windows PowerShell .NET combinada à elegância da linguagem C# me fez abordar o REPL do C# como um substituto para o Windows PowerShell. Após iniciar o csi.exe, imediatamente experimentei comandos como cd, dir, ls, pwd, cls, alias e outros. Basta dizer que fiquei decepcionado, pois nenhum deles funcionou. Depois de refletir sobre a experiência e discuti-la com a equipe do C#, percebi que a substituição do Windows PowerShell não era o enfoque da equipe na versão 1. Além disso, trata-se do .NET Framework, e portanto ele dá suporte à extensibilidade adicionando suas próprias funções aos comandos anteriores e até mesmo atualizando a implementação do script do C# no Roslyn. Imediatamente comecei a definir funções para esses comandos. O início dessa biblioteca está disponível para download no GitHub em github.com/CSScriptEx.
Se você busca uma CLI do C# mais funcional que dê suporte à lista de comandos anterior desde o início, considere o ScriptCS em scriptcs.net (também no GitHub, em github.com/scriptcs). Ele também tira proveito do Roslyn e inclui alias, cd, clear, cwd, exit, help, install, references, reset, scriptpacks, usings e vars. Observe que, com o ScriptCS, hoje o prefixo de comando consiste em dois-pontos (como em :reset) em vez de uma cerquilha (como em #reset). Como um bônus adicional, o ScriptCS também adiciona suporte a arquivos CSX, na forma de colorização e IntelliSense, para o Visual Studio Code.
Conclusão
Pelo menos por enquanto, o objetivo da interface REPL do C# não é substituir o Windows PowerShell ou mesmo o cmd.exe. Se você o abordar dessa forma no início, se decepcionará. Em vez disso, sugiro que você aborde os scripts do C# e as CLIs do REPL como substituições leves para o Visual Studio | Novo Projeto: UnitTestProject105 ou dotnetfiddle.net, que tem um propósito semelhante. Essas são maneiras direcionadas do C# e do .NET para aumentar sua compreensão das APIs do .NET e da linguagem. O REPL do C# fornece um meio de codificar trechos curtos ou unidades de programa em que você pode trabalhar até que estejam prontos para serem recortados e colados em programas maiores. Ele permite que você escreva scripts mais extensos cuja sintaxe é validada (mesmo para pequenas coisas como incompatibilidades no uso de maiúsculas) à medida que você escreve o código, em vez de forçá-lo a executar o script apenas para descobrir que digitou algo incorretamente. Depois que você entende sua finalidade, os scripts do C# e suas janelas interativas tornam-se um prazer, a ferramenta que você estava procurando desde a versão 1.0.
Embora o REPL do C# e os script do C# sejam interessantes por conta própria, lembre-se de que eles também fornecem um ponto de partida para uma estrutura de extensão para seu próprio aplicativo, de forma semelhante ao VBA (Visual Basic for Applications). Com uma janela interativa e suporte a scripts do C#, você pode imaginar um mundo (não tão distante) no qual é possível adicionar "macros" do .NET a seus próprios aplicativos novamente, sem inventar uma linguagem, um analisador e um editor personalizados. Esse seria um recurso herdado do COM que valeria a pena trazer para o mundo moderno no momento certo.
Mark Michaelis é fundador da IntelliTect, onde atua como arquiteto técnico principal e instrutor. Há quase 20 anos trabalha como Microsoft MVP, e é Diretor Regional da Microsoft desde 2007. Michaelis atua em diversas equipes de análise de design de software da Microsoft, incluindo C#, Microsoft Azure, SharePoint e Visual Studio ALM. Ele dá palestras em conferências de desenvolvedores e escreveu diversos livros, incluindo o mais recente, "Essential C# 6.0 (5th Edition)" (itl.tc/EssentialCSharp). Você pode entrar em contato com ele pelo Facebook, em facebook.com/Mark.Michaelis, pelo seu blog IntelliTect.com/Mark, no Twitter: @markmichaelis ou pelo email mark@IntelliTect.com.
Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Kevin Bost e Kasey Uhlenhuth