Share via


CLR

Novidades na Base Class Library do NET 4.5

Immo Landwerth

 

A BCL (Base class library) do .NET Framework 4.0 diz respeito a fundamentos. Embora algumas das construções básicas sejam estáveis e não se alterem muito (por exemplo, System.Int32 e System.String), a Microsoft ainda está investindo bastante nessa área. Este artigo aborda os grandes (e alguns pequenos) aperfeiçoamentos feitos na BCL do .NET Framework 4.5.

Ao ler este artigo, lembre-se de que é baseado na versão beta do .NET Framework 4.5, não no produto final e nas APIs, portanto, os recursos estão sujeitos a alteração.

Se desejar uma visão geral de outras áreas do .NET Framework, como WCF (Windows Communication Foundation) ou WPF (Windows Presentation Foundation), consulte a página da Biblioteca MSDN, “Novidades do .NET Framework 4.5 Beta” (bit.ly/p6We9u).

Programação assíncrona simplificada

A utilização de E/S assíncrona tem várias vantagens. Ajuda a impedir o bloqueio da interface do usuário e pode reduzir o número de threads que o sistema operacional tem de usar. E ainda, é provável que você não esteja aproveitando isso, pois a programação assíncrona costumava ser muito complexa. O maior problema foi que o APM (Asynchronous Programming Model) anterior foi projetado com base em pares de métodos Begin/End. Para ilustrar como esse padrão funciona, considere o seguinte método síncrono simples que copia um fluxo:

public void CopyTo(Stream source, Stream destination)
{
  byte[] buffer = new byte[0x1000];
  int numRead;
  while ((numRead = source.Read(buffer, 0, buffer.Length)) != 0)
  {
    destination.Write(buffer, 0, numRead);
  }
}

Para tornar esse método assíncrono utilizando o APM anterior, você teria de escrever o código mostrado na Figura 1.

Figura 1. Copiando um fluxo da maneira assíncrona antiga

public void CopyToAsyncTheHardWay(Stream source, Stream destination)
{
  byte[] buffer = new byte[0x1000];
  Action<IAsyncResult> readWriteLoop = null;
  readWriteLoop = iar =>
  {
    for (bool isRead = (iar == null); ; isRead = !isRead)
    {
      switch (isRead)
      {
        case true:
          iar = source.BeginRead(buffer, 0, buffer.Length, 
            readResult =>
          {
            if (readResult.CompletedSynchronously) return;
            readWriteLoop(readResult);
          }, null);
          if (!iar.CompletedSynchronously) return;
          break;
        case false:
          int numRead = source.EndRead(iar);
          if (numRead == 0)
          {
            return;
          }
          iar = destination.BeginWrite(buffer, 0, numRead, 
            writeResult =>
          {
            if (writeResult.CompletedSynchronously) return;
            destination.EndWrite(writeResult);
            readWriteLoop(null);
          }, null);
          if (!iar.CompletedSynchronously) return;
          destination.EndWrite(iar);
          break;
        }
      }
  };
  readWriteLoop(null);
}

Claramente, a versão assíncrona não é tão simples de entender como a equivalente síncrona. A intenção é difícil de extrair do código clichê necessário para fazer construções básicas de linguagem de programação, como loops, funcionar quando delegados estão envolvidos. Se ainda não estiver apavorado, tente adicionar cancelamento e manipulação de exceção.

Felizmente, essa versão da BCL traz um novo modelo de programação assíncrona que se baseia em Task e Task<T>. O C# e o Visual Basic adicionaram o suporte de linguagem de primeira classe incluindo as palavras-chave async e await (a propósito, a F# já tinha suporte de linguagem via fluxos de trabalho assíncronos e, de fato, serviu como inspiração para esse recurso). Como resultado, os compiladores agora cuidarão da maior parte, se não de todo, do código clichê que você tinha de escrever. O novo suporte de linguagem, além de certas adições de APIs ao .NET Framework, trabalha junto para tornar a escrita de métodos assíncronos virtualmente tão simples como escrever código síncrono. Veja você mesmo; para tornar o método CopyTo assíncrono são necessárias apenas as seguintes alterações realçadas:

public async Task CopyToAsync(Stream source, Stream destination)
{
  byte[] buffer = new byte[0x1000];
  int numRead;
  while ((numRead = await 
     source.ReadAsync(buffer, 0, buffer.Length)) != 0)
  {
    await destination.WriteAsync(buffer, 0, numRead);
  }
}

Há muitas coisas interessantes a dizer sobre a programação assíncrona utilizando os novos recursos de linguagem, mas gostaria de me concentrar no impacto na BCL e em você, o consumidor da BCL. No entanto, se estiver muito curioso, prossiga e leia a página da Biblioteca MSDN, “Programação assíncrona com Async e Await (C# e Visual Basic)” (bit.ly/nXerAc).

Para criar suas próprias operações assíncronas, você precisa de blocos de construção de baixo nível. Com base neles, você pode criar métodos mais complexos como o método CopyToAsync mostrado anteriormente. Esse método precisa apenas dos métodos ReadAsync e WriteAsync da classe Stream como blocos de construção. A Figura 2 lista algumas das mais importantes APIs assíncronas adicionadas à BCL.

Figura 2. Métodos assíncronos na BCL

Tipo Métodos
System.IO.Stream

ReadAsync

WriteAsync

FlushAsync

CopyToAsync

System.IO.TextReader

ReadAsync

ReadBlockAsync

ReadLineAsync

ReadToEndAsync

System.IO.TextWriter

WriteAsync

WriteLineAsync

FlushAsync

Observe que não adicionamos versões assíncronas de APIs que tinham uma granularidade muito pequena, como TextReader.Peek. O motivo é que APIs assíncronas também adicionam alguma sobrecarga e queríamos impedir que os desenvolvedores acidentalmente fossem na direção errada. Isso também significa que especificamente decidimos contra fornecer versões assíncronas de métodos na BinaryReader ou BinaryWriter. Para usar essas APIs, recomendamos usar Task.Run para iniciar uma nova operação assíncrona e, então, usar APIs síncronas de dentro dessa operação. No entanto, não faça isso por chamada de método. A orientação geral é: tente tornar sua operação assíncrona tão robusta quanto possível. Por exemplo, se desejar ler 1.000 Int32s de um fluxo usando BinaryReader, é melhor executar e esperar que uma tarefa leia de forma síncrona todos os 1.000 do que executar individualmente e esperar 1.000 tarefas, cada uma delas lendo apenas um único Int32.

Se desejar saber mais sobre a escrita de código assíncrono, recomendo ler o blog de programação paralela com .NET (blogs.msdn.com/b/pfxteam).

Interfaces de coleção somente leitura

Uma das antigas solicitações de recursos para a BCL era interfaces de coleção somente leitura (não confunda com coleções imutáveis; consulte “Noções diferentes de coleções somente leitura” na página 22 para obter detalhes). Nossa posição tem sido que esse aspecto particular (ser somente leitura) é mais bem modelado usando o padrão de recurso opcional. Nesse padrão, uma API é fornecida permitindo que os consumidores testem se um determinado recurso não tem suporte e gerem uma NotSupportedException.

O benefício desse padrão é que ele precisa de menos tipos, pois não há necessidade de modelar combinações de recursos. Por exemplo, a classe Stream fornece vários recursos e todos eles são expressos via acessadores get boolianos (CanSeek, CanRead, CanWrite e CanTimeout). Isso permite que a BCL tenha um único tipo de fluxo e ainda assim dê suporte a toda combinação dos recursos de streaming.

Ao longo dos anos, chegamos à conclusão que adicionar uma interface de coleção somente leitura vale a complexidade adicional. Primeiramente, gostaria de mostrar as interfaces e, em seguida, discutir os recursos que ela oferecem. A Figura 3 mostra um diagrama de classes Visual Studio das interfaces de coleção existentes (mutáveis); a Figura 4 mostra as interfaces somente leitura correspondentes.

Mutable Collection Interfaces
Figura 3 Interfaces de coleção mutáveis

Read-Only Collection Interfaces
Figura 4 Interfaces de coleção somente leitura

Observe que IReadOnlyCollection<T> não entrou na versão beta do .NET Framework 4.5, portanto, não fique surpreso se não o encontrar. Na versão beta, IReadOnlyList<T> e IReadOnlyDictionary<TKey,TValue> derivam diretamente de IEnumerable<T> e cada interface define sua própria propriedade Count.

IEnumerable<T> é covariante. Isso significa que se um método aceita um IEnumerable<Shape> é possível chamá-lo com um IEnumerable<Circle> (supondo que Circle é derivado de Shape). Isso é útil em cenários com hierarquias de tipo e algoritmos que podem operar em tipos específicos, como um aplicativo que pode desenhar várias formas. IEnumerable<T> é suficiente para a maioria dos cenários que lidam com coleções de tipos, mas algumas vezes mais poder do que o fornecido é necessário:

  1. Materialization.IEnumerable<T> não permite que você expresse se a coleção já está disponível (“materializada”) ou se ela é computada toda vez que você itera nela (por exemplo, se ela representar uma consulta LINQ). Quando um algoritmo precisar de várias iterações na coleção, isso poderá resultar em degradação de desempenho se a computação da sequência for cara; poderá também causar bugs sutis por causa de incompatibilidades de identidade quando os objetos estiverem sendo gerados novamente em passos subsequentes.
  2. Count.IEnumerable<T> não fornece uma contagem. De fato, pode mesmo não ter uma, pois pode ser uma sequência infinita. No entanto, em muitos casos, o método de extensão estático Enumerable.Count é bom o suficiente. Primeiro, ele classifica como caso especial conhecidos tipos de coleção como ICollection<T> para impedir a iteração da sequência inteira; segundo, em muitos casos, a computação do resultado não é cara. No entanto, dependendo do tamanho da coleção, isso pode fazer diferença.
  3. Indexing.IEnumerable<T> não permite acesso aleatório aos itens. Alguns algoritmos, como a classificação rápida, dependem de poder chegar a um item por seu índice. Novamente, há um método de extensão estático (Enumerable.ElementAt) que usa um caminho de código otimizado se o determinado enumerável estiver apoiado por um IList<T>. No entanto, se a indexação for usada em um loop, uma verificação linear poderá ter implicações de desempenho desastrosas, pois tornará seu ótimo algoritmo O(n) em um algoritmo O(n2). Portanto, se precisar de acesso aleatório, bem, você realmente precisa de acesso aleatório.

Assim, por que não usar simplesmente ICollection<T>/IList<T> em vez de IEnumerable<T>? Porque você perde covariância e porque não pode mais diferenciar entre métodos que somente leem as coleções e métodos que também modificam as coleções, algo que se torna cada vez mais importante quando você usa programação assíncrona ou multithreading em geral. Em outras palavras, você tem o melhor dos dois mundos.

Inserir IReadOnlyCollection<T> e IReadOnlyList<T>. IReadOnlyCollection<T> é basicamente o mesmo que IEnumerable<T>, mas adiciona uma propriedade Count. Isso permite criar algoritmos que podem expressar a necessidade de coleções materializadas ou coleções com um tamanho conhecido e finito. IReadOnlyList<T> expande ainda mais adicionando um indexador. As duas interfaces são covariantes, o que significa que se um método aceita um IReadOnlyList<Shape>, você pode chamá-lo com um List<Circle>:

class Shape { /*...*/ }
class Circle : Shape { /*...*/ }
void LayoutShapes(IReadOnlyList<Shape> shapes) { /*...*/ }
void LayoutCircles()
{
  List<Circle> circles = GetCircles();
  LayoutShapes(circles);
}

Infelizmente, nosso sistema de tipo não permite fazer tipos de covariante T a menos que não haja nenhum método que aceite T como entrada. Portanto, não podemos adicionar um método IndexOf a IReadOnlyList<T>. Acreditamos que esse seja um pequeno sacrifício comparado a não ter suporte para covariância.

Todas as nossas implementações de coleção interna, como matrizes, List<T>, Collection<T> e ReadOnlyCollection<T>, também implementam as interfaces de coleção somente leitura. Como qualquer coleção agora pode ser tratada como somente leitura, os algoritmos podem declarar sua intenção mais precisamente sem limitar seu nível de reutilização. Eles podem ser usados em todos os tipos de coleções. No exemplo anterior, o consumidor de LayoutShapes poderia passar um List<Circle>, mas LayoutShapes também aceitaria uma matriz de Circle ou um Collection<Circle>.

Um outro benefício desses tipos de coleção é a excelente experiência que eles oferecem para o trabalho com o Tempo de Execução do Windows. O Tempo de Execução do Windows fornece seus próprios tipos de coleção, como Windows.Foundation.Collections.IIterable<T> e Windows.Foundation.Collections.IVector<T>. A camada de metadados CLR os projeta diretamente para os tipos de dados BCL correspondentes. Por exemplo, ao consumir o Tempo de Execução do Windows no .NET Framework, IIterable<T> torna-se IEnumerable<T> e IVector<T> torna-se IList<T>. Na verdade, um desenvolvedor usando Visual Studio e IntelliSense não poderia mesmo dizer que o Tempo de Execução do Windows tenha tipos diferentes de coleção. Como o Tempo de Execução do Windows também fornece versões somente leitura (como IVectorView<T>), as novas interfaces somente leitura completam o quadro de modo que todos os tipos de coleção possam facilmente ser compartilhados entre o .NET Framework e o Tempo de Execução do Windows.

Noções diferentes de coleções somente leitura

  • Mutável (ou Não somente leitura) O tipo de coleção mais comum no mundo do .NET. São coleções como List<T> que permitem ler, além de adicionar, remover e alterar itens.
  • Somente leitura São coleções que não podem ser modificadas de fora. No entanto, essa noção de coleções não garante que seu conteúdo nunca será alterado. Por exemplo, as coleções de chaves e valores de um dicionário não podem ser atualizadas diretamente, mas adicionar ao dicionário indiretamente atualiza as coleções de chaves e valores.
  • Imutável São coleções que, uma vez criadas, estão garantidas de nunca serem alteradas. É uma ótima propriedade para multithreading. Se uma estrutura de dados complicada for totalmente imutável, ela poderá ser sempre passada com segurança para um operador em segundo plano. Você não precisa se preocupar com alguém fazendo modificações nela ao mesmo tempo. Esse tipo de coleção não é oferecido pela BCL do Microsoft .NET Framework no momento.
  • Freezable Essas coleções se comportam como mutáveis até serem congeladas. Daí, comportam-se como coleções imutáveis. Embora a BCL não defina essas coleções, é possível encontrá-las no Windows Presentation Foundation.

Andrew Arnott escreveu uma excelente postagem de blog que descreve as diferentes noções de coleções com mais detalhes (bit.ly/pDNNdM).

Suporte a arquivos .zip

Uma outra solicitação comum ao longo dos anos foi o suporte para leitura e gravação de arquivos .zip normais. O .NET Framework 3.0 e posteriores dão suporte para leitura e gravação de arquivos na Open Packaging Convention (bit.ly/ddsfZ7). No entanto, o System.IO.Packaging foi desenvolvido para dar suporte a essa especificação específica e, geralmente, não pode ser usada para processar arquivos .zip comuns.

Essa versão adiciona suporte a compactação de primeira classe via System.IO.Com­pression.ZipArchive. Além disso, corrigimos alguns problemas antigos com o desempenho e a qualidade da compactação em nossa implementação de DeflateStream A partir do .NET Framework 4.5, a classe Deflate­Stream usa a popular biblioteca zlib. Como resultado, fornece uma melhor implementação do algoritmo deflate e, na maioria dos casos, um arquivo compactado menor do que em versões anteriores do .NET Framework.

A extração de um arquivo morto inteiro em disco precisa apenas de uma única linha:

ZipFile.ExtractToDirectory(@"P:\files.zip", @"P:\files");

Também asseguramos que operações típicas não necessitem da leitura do arquivo morto inteiro na memória. Por exemplo, extrair um único arquivo de um grande arquivo morto pode ser feito da seguintes maneira:

using (ZipArchive zipArchive = 
  ZipFile.Open(@"P:\files.zip", ZipArchiveMode.Read))
{
  foreach (ZipArchiveEntry entry in zipArchive.Entries)
  {
    if (entry.Name == "file.txt")
    {
      using (Stream stream = entry.Open())
      {
        ProcessFile(stream);
      }
    }
  }     
}

Nesse caso, a única parte carregada na memória é o sumário do arquivo .zip. O arquivo sendo extraído é totalmente transmitido; isso é, não precisa caber na memória. Isso permite processar arquivos .zip arbitrariamente grandes, mesmo que a memória seja limitada.

Criar arquivos .zip funciona da mesma maneira. Para criar um arquivo .zip de um diretório, é preciso escrever apenas uma única linha:

ZipFile.CreateFromDirectory(@"P:\files", @"P:\files.zip");

Certamente, você também pode construir manualmente um arquivo .zip, o que lhe dá total controle sobre a estrutura interna. O código a seguir mostra como criar um arquivo .zip em que somente arquivos de código-fonte C# são adicionados (além disso, o arquivo .zip agora também tem um subdiretório chamado SourceCode):

IEnumerable<string> files = 
  Directory.EnumerateFiles(@"P:\files", "*.cs");
using (ZipArchive zipArchive = 
  ZipFile.Open(@"P:\files.zip", ZipArchiveMode.Create))
{
  foreach (string file in files)
  {
    var entryName = Path.Combine("SourceCode", Path.GetFileName(file));
    zipArchive.CreateEntryFromFile(file, entryName);
  }
}

Utilizando fluxos também é possível construir arquivos .zip em que as entradas não são apoiadas por um arquivo real. O mesmo é válido para o próprio arquivo .zip. Por exemplo, em vez do ZipFile.Open, é possível usar o construtor ZipArchive que considera um fluxo. Isso poderia ser usado, por exemplo, para criar um servidor Web que cria arquivos .zip dinamicamente de conteúdo armazenado em um banco de dados e também grava os resultados diretamente no fluxo de resposta, em vez de no disco.

Um detalhe sobre o projeto de APIs vale a pena explicar um pouco mais. Provavelmente você notou que os métodos de conveniência estáticos são definidos na classe ZipFile, enquanto a instância real tem o tipo ZipArchive. Por que isso?

A partir do .NET Framework 4.5, projetamos as APIs com a portabilidade em mente. Algumas plataformas .NET não dão suporte a caminhos de arquivo, como .NET para aplicativos estilo Metro no Windows 8. Nessa plataforma, o acesso ao sistema de arquivos é orientado para permitir um modelo baseado em recurso. Para ler ou gravar arquivos, não é mais possível usar APIs do Win32 normais; em vez disso, deve-se usar APIs do Tempo de Execução do Windows.

Como lhes mostrei, a própria funcionalidade .zip, definitivamente, não precisa de caminhos de arquivo. Por isso, dividimos a funcionalidade em duas partes:

  1. System.IO.Compression.dll. Esse assembly contém funcionalidade .zip para fins gerais. Não dá suporte a caminhos de arquivo. A classe principal nesse assembly é ZipArchive.
  2. System.IO.Compression.FileSystem.dll. Esse assembly fornece uma classe estática ZipFile que define os métodos de extensão e os auxiliares estáticos. Essas APIs ampliam a funcionalidade .zip com métodos de conveniência em plataformas .NET que dão suporte aos caminhos do sistema de arquivos.

Para saber mais sobre a escrita de código .NET portátil, consulte a página Bibliotecas de classes portáteis na Biblioteca MSDN em bit.ly/z2r3eM.

Outros aperfeiçoamentos

É claro que há sempre mais a ser dito. Depois de trabalhar em um lançamento por tanto tempo, dar nome aos mais importantes recursos algumas vezes é como ser pedido para dar nome a seu filho favorito. A seguir, gostaria de destacar algumas áreas que valem a pena mencionar, mas que não tiveram o espaço merecido nesse artigo:

AssemblyMetadataAttribute Uma coisa que aprendemos no .NET Framework sobre atributos é que não importa quantos os tenha, ainda precisa de mais (a propósito, o .NET Framework 4 já tinha mais do que 300 atributos em nível de assembly). AssemblyMetadataAttribute é um atributo de assembly para fins gerais que permite associar um par chave-valor baseado em cadeia de caracteres a um assembly. Isso pode ser usado para apontar para a página inicial do produto ou para o rótulo de controle de versão que corresponde à fonte da qual o assembly foi criado.

WeakReference<T> O tipo WeakReference não genérico existente tem dois problemas: primeiro, ele força os consumidores a converter sempre que o destino precisar ser acessado. E o mais importante, ele tem uma falha de projeto que torna seu uso propenso a condições de corrida: ele expõe uma API para verificar se o objeto está ativo (IsAlive) e uma API diferente para chegar ao objeto real (Target). WeakReference<T> corrige esse problema fornecendo a API única TryGetTarget, que executa as duas operações de maneira atômica.

Comparer<T>.Create(Comparison<T>) A BCL fornece duas maneiras de implementar comparadores de coleções. Uma é via a interface IComparer<T>; a outra é o Comparison<T> delegado. Converter um IComparer<T> em Comparison<T> é simples. A maioria das linguagens oferece uma conversão implícita de um método para um tipo delegado, de modo que você possa facilmente construir o Comparison<T> do método IComparer<T> Compare. No entanto, a outra forma precisava que você mesmo implementasse um IComparer<T>. No .NET Framework 4.5 adicionamos um método Create estático ao Comparer<T> que, dado um Comparison<T>, lhe dá uma implementação do IComparer<T>.

ArraySegment<T> No .NET Framework 2.0 adicionamos um struct ArraySegment<T>, que lhe permite representar um subconjunto de uma determinada matriz sem copiar. Infelizmente, ArraySegment<T> não implementou nenhuma dessas interfaces de coleção, o que impediu que você o passasse para os métodos que operam nas interfaces de coleção. Nessa versão, corrigimos isso e agora ArraySegment<T> implementa IEnumerable, IEnumerable<T>, ICollection<T> e IList<T>, além de IReadOnlyCollection<T> e IReadOnlyList<T>.

SemaphoreSlim.WaitAsync Esse é o único primitivo de sincronização que dá suporte a esperar que o bloqueio seja feito. Se desejar saber mais sobre a lógica, consulte “Novidades do paralelismo na versão beta do .NET 4.5” (bit.ly/AmAUIF).

ReadOnlyDictionary<TKey,TValue> Desde o .NET Framework 2.0, a BCL forneceu ReadOnlyCollection<T>, que serve como um wrapper somente leitura em torno de uma determinada instância de coleção. Isso permite que os implementadores de modelos de objeto exponham coleções que o consumidor não pode alterar. Nessa versão, adicionamos o mesmo conceito para dicionários.

BinaryReader, BinaryWriter, StreamReader, StreamWriter: opção para não descartar o fluxo subjacente Todas as classes mais altas de leitor e gravador aceitam uma instância de fluxo em seus construtores. Em versões anteriores, isso significava que a propriedade desse fluxo passava para a instância de leitor/gravador, o que significava que descartar o leitor/gravador também descartava o fluxo subjacente. Tudo é muito bom e legal quando se tem um único leitor/gravador, mas é mais difícil em casos em que você precisa compor várias APIs diferentes que operam todas em fluxos, mas precisam usar leitores/gravadores como parte de sua implementação. A melhor coisa que você poderia fazer em versões anteriores seria não descartar o leitor/gravador e deixar um comentário na fonte explicando o problema (essa abordagem também forçava que você liberasse manualmente o gravador para evitar a perda de dados). O .NET Framework 4.5 permite que você expresse esse contrato usando um construtor de leitor/gravador que assume o parâmetro leaveOpen em que é possível especificar explicitamente que o leitor/gravador não deve descartar o fluxo subjacente.

Console.IsInputRedirected, Console.IsOutputRedirected e Console.IsErrorRedirected Programas de linha de comando dão suporte ao redirecionamento de entrada e saída. Para a maioria dos aplicativos, isso é transparente. No entanto, algumas vezes é desejado um comportamento diferente quando o redirecionamento está ativo. Por exemplo, saída do console colorida não é útil e definir a posição do cursor não terá êxito. Essas propriedades permitem consultar se qualquer dos fluxos padrão foi redirecionado.

Console.OutputEncoding e Console.InputEncoding agora podem ser definidos como Encoding.Unicode Definir Console.OutputEncoding como Encoding.Unicode permite que o programa escreva caracteres que não poderiam ser representados na página de código do OEM associada ao console. Esse recurso também facilita a exibição de texto em diversos scripts no console. Mais detalhes estarão disponíveis na futura documentação revisada na Biblioteca MSDN para a classe Console.

ExceptionDispatchInfo O tratamento de erro é um importante aspecto no desenvolvimento de componentes da estrutura. Algumas vezes lançar exceções (no C# via “throw;”) não é suficiente, pois isso só pode acontecer dentro de um manipulador de exceção. Alguns componentes da estrutura, como a infraestrutura Task, têm de lançar novamente a exceção posteriormente (por exemplo, depois de realizar o marshaling de volta ao thread original). No passado, isso significava que o rastreamento de pilha original e a classificação do Relatório de Erros do Windows (também conhecido como buckets Watson) eram perdidos, pois lançar o mesmo objeto de exceção novamente simplesmente substituiria essas informações. ExceptionDispatchInfo permite capturar um objeto de exceção existente e lançá-lo novamente sem perder qualquer informação valiosa registrada no objeto de exceção.

Regex.Timeout As expressões regulares são uma ótima maneira de validar a entrada. No entanto, não é amplamente conhecido que certas expressões regulares podem ser incrivelmente caras de computar quando aplicadas a entradas de texto específicas, isso é, elas têm complexidade de tempo exponencial. Isso é especialmente problemático em ambientes de servidor em que as expressões regulares reais estão sujeitas à configuração. Porque é difícil, se não impossível, prever o comportamento em tempo de execução de uma determinada expressão regular; a abordagem mais conservadora para esses casos é aplicar uma restrição em quanto tempo o mecanismo Regex tentará corresponder a uma determinada entrada. Por esse motivo, Regex agora tem várias APIs que aceitam um tempo limite: Regex.IsMatch, Regex.Match, Regex.Matches, Regex.Split e Regex.Replace.

Participe

Os recursos discutidos neste artigo estão disponíveis na versão beta do Visual Studio 11. Atendem aos nossos altos padrões de software de pré-lançamento, portanto, damos suporte a eles em ambientes de produção. Você pode baixar a versão beta de bit.ly/9JWDT9. Como é software de pré-lançamento, estamos interessados em receber seus comentários, se encontrou algum problema (connect.microsoft.com/visualstudio) ou se tem ideias para novos recursos (visualstudio.uservoice.com). Gostaria também de sugerir que assinem o blog de minha equipe em blogs.msdn.com/b/bclteam para que fiquem informados sobre alterações ou anúncios futuros.

Immo Landwerth é gerente de programas da equipe do CLR na Microsoft e trabalha com a BCL do Microsoft .NET Framework, o projeto de APIs e as bibliotecas de classe portáteis. Você pode entrar em contato com ele através do blog da equipe da BCL em blogs.msdn.com/b/bclteam.

Agradecemos aos seguintes especialistas técnicos pela revisão deste artigo: Nicholas Blumhardt, Greg Paperin, Daniel Plaisted, Evgeny Roubinchtein, Alok Shriram, Chris Szurgot, Stephen Toub e Mircea Trofin