Este artigo foi traduzido por máquina.

C# 4.0

Novos recursos do C# no .NET Framework 4

Chris Burrows

Desde seu lançamento inicial em 2002, a linguagem de programação c# foi aprimorado para permitir que os programadores criem esclarecer, mais passível de manutenção de código. Os aprimoramentos de tem vindo da adição de recursos como, por exemplo, tipos genéricos, tipos de valor anulável, expressões lambda, métodos de iterador, classes parciais e uma longa lista de outras construções de linguagem úteis. Além disso, muitas vezes, as alterações foram acompanhadas, oferecendo suporte correspondente de bibliotecas do Microsoft .NET Framework.

Essa maior utilização tendência continua no c# 4. 0. As adições realizam tarefas comuns envolvendo os tipos genéricos, interoperabilidade de legado e trabalhar com modelos de objeto dinâmico muito mais simples. Este artigo tem como objetivo dar uma pesquisa de alto nível desses novos recursos. Eu vai começar com variação genérica e, em seguida, examine os recursos de interoperabilidade herdados e dinâmicos.

A covariância e Contravariance

A covariância e contravariance melhor são introduzidas com um exemplo e o melhor é na estrutura. Em System.collections.generic, IEnumerable <T> e IEnumerator <T> representam, respectivamente, um objeto que é uma seqüência de T e o enumerador (ou iterador) que faz o trabalho de iteração da seqüência. Essas interfaces fez muito pesado por um longo tempo, porque eles oferecem suporte a implementação da construção de loop foreach. No c# 3. 0, eles ficaram ainda mais proeminentes por causa do seu papel central no LINQ e LINQ to Objects – eles são as interfaces .net para representar as seqüências.

Assim, se você tiver uma hierarquia de classes com, digamos, um tipo de funcionário e gerente de digitar que deriva dela (gerentes são funcionários, afinal de contas), em seguida, o que você esperaria o código a seguir para fazer?

IEnumerable<Manager> ms = GetManagers();
IEnumerable<Employee> es = ms;

Parece que um deve ser capaz de tratar uma seqüência de gerentes de como se fosse uma seqüência de funcionários. Mas, no c# 3. 0, a atribuição irá falhar; o compilador dirá que não há nenhuma conversão. Afinal, ela não tem ideia servem a semântica de IEnumerable <T>. Isso pode ser qualquer interface, portanto, para qualquer interface arbitrário IFoo <T>, por que um IFoo <Manager> seria substituível mais ou menos para um IFoo <Employee>?

No c# 4. 0, no entanto, a atribuição funciona como IEnumerable <T>, juntamente com algumas outras interfaces foi alterada, uma alteração habilitada para o novo suporte em c# para a covariância de parâmetros de tipo.

IEnumerable <T> está qualificado para ser mais especial que o IFoo arbitrário <T> porque, embora não seja óbvio à primeira vista, os membros que usam o parâmetro de tipo T (GetEnumerator em IEnumerable <T>) e a propriedade Current no IEnumerator <T> realmente usam T apenas na posição de um valor de retorno. Assim, você obtém apenas um gerente de fora da seqüência e você nunca colocar um.

Por outro lado, pense em <T> da lista. Fazer uma lista de <Manager> substituível para uma lista de <Employee> seria um desastre, por causa das seguintes opções:

List<Manager> ms = GetManagers();
List<Employee> es = ms; // Suppose this were possible
es.Add(new EmployeeWhoIsNotAManager()); // Uh oh

Como isso mostra, uma vez que você acha que você está olhando uma lista de <Employee>, você pode inserir qualquer funcionário. Mas a lista em questão, na verdade, uma lista de <Manager> para que inserção de um gerente-não deve falhar. Se você permitir isso, você tenha perdido da segurança de tipos. Lista <T> não pode ser covariant em t.

O novo recurso de linguagem do c# 4. 0, em seguida, é a capacidade de definir os tipos, tais como o IEnumerable novo <T> admitir conversões entre si quando os parâmetros de tipo em questão deve arcar com alguma relação uns aos outros. Isso é que os desenvolvedores do .NET Framework que escreveu o IEnumerable <T> usado e isso é o que seu código é semelhante (simplificada, é claro):

public interface IEnumerable<out T> { /* ... */ }

Observe a palavra-chave para fora, modificando a definição do parâmetro de tipo, t. Quando o compilador vê isso, ele irá Mark T como covariant e verificar se, na definição da interface, todos os usos de T de snuff (em outras palavras, o que eles são usados em check-out somente de posições – é por isso que esta palavra-chave foi separado).

Por que isso é chamado de covariância? Bem, é mais fácil de ver quando começar a desenhar setas. Para ser concreto, let’s usam os tipos de gerente e funcionário. Como há uma relação de herança entre essas classes, há uma conversão de referência implícita de Gerenciador para funcionário:

Funcionário do gerente →

E agora, devido a anotação de T em IEnumerable < out T >, há também uma referência implícita a conversão de IEnumerable <Manager> em IEnumerable <Employee>. Trata-se para a anotação fornece:

IEnumerable <Manager> → IEnumerable <Employee>

Isso se chama a covariância, porque as setas em cada um dos dois exemplos apontam na mesma direção. Começamos com dois tipos, gerente e funcionário. Fizemos novos tipos de check-out deles, IEnumerable <Manager> e IEnumerable <Employee>. Os novos tipos de conversão da mesma forma como os antigos.

Contravariance é quando isso acontece para trás. Pode prever o que isso pode acontecer quando o parâmetro de tipo T, é usado apenas como entrada, e você estaria correta. Por exemplo, o namespace System contém uma interface IComparable <T>, que tem um único método chamado CompareTo de chamada:

public interface IComparable<in T> { 
  bool CompareTo(T other); 
}

Se você tiver um IComparable <Employee>, deve ser capaz de tratá-la como se fosse um IComparable <Manager> porque a única coisa que você pode fazer é colocar os funcionários em à interface. Como gerente de um funcionário, colocando um gerente no deve funcionar, e ele faz. A palavra-chave em modifica T nesse caso, e esse cenário funcione corretamente:

IComparable<Employee> ec = GetEmployeeComparer();
IComparable<Manager> mc = ec;

Isso é chamado de contravariance porque a seta foi revertida neste momento:

Funcionário do gerente →
IComparable <Manager> ← IComparable <Employee>

Portanto, o recurso de idioma aqui é bastante simples para resumir: Você pode adicionar a palavra-chave na ou check-out sempre que você define um parâmetro de tipo e ao fazer isso tão oferece conversões adicionais grátis. No entanto, existem algumas limitações.

Em primeiro lugar, isso funciona com interfaces genéricas e delega somente. Você não pode declarar um parâmetro de tipo genérico em class ou struct dessa maneira. Uma maneira fácil de racionalizar isso é que a delegados são muito parecido com interfaces que tenham apenas um método, e em qualquer caso, classes normalmente seria inadequados para esta tratamento por causa dos campos. Você pode pensar em qualquer campo da classe genérica como entrada e de uma saída, dependendo se você escreve para ele ou ler a partir dele. Se esses campos envolvem parâmetros de tipo, os parâmetros podem ser nem covariant ou contravariant.

Segundo, sempre que você tem uma interface ou delegar com um parâmetro de tipo covariant ou contravariant são concedidas as conversões de novas no que digite apenas quando os argumentos de tipo, o uso da interface (e não sua definição), são tipos de referência. Por exemplo, como int é um tipo de valor, o IEnumerator <int> não converter IEnumerator <object>, mesmo que parece que ele deve:

IEnumerator <int> image: right arrow with slash  IEnumerator <object>

O motivo desse comportamento é que a conversão deve preservar a representação de tipo. Se a conversão do objeto de int eram permitida, chamando a propriedade Current no resultado poderia ser impossível, pois o valor de tipo int tem uma representação diferente na pilha que faz uma referência ao objeto. Todos os tipos de referência têm a mesma representação na pilha, no entanto, para que os argumentos de tipo único que são tipos de referência geram essas conversões extras.

Muito provavelmente, a maioria dos desenvolvedores do c# Felizmente usará esse novo recurso de linguagem — eles obterá mais conversões de tipos de estrutura e menos erros de compilador ao uso de alguns tipos do .NET Framework (IEnumerable <T> IComparable <T>, Func <T>, ação <T>, entre outros). E, na verdade, qualquer pessoa que criar uma biblioteca com delegados e interfaces genéricas é livre para usar o novo em parâmetros de tipo quando for apropriado para facilitar a vida útil para seus usuários.

A propósito, esse recurso exige suporte a tempo de execução — mas o suporte foi sempre lá. Formatar inativo para várias versões, no entanto, pois nenhum idioma feitas usá-lo. Além disso, as versões anteriores do c# permitiam algumas conversões limitados que estavam contravariant. Especificamente, eles permitem fazer delegações de métodos com tipos de retorno compatíveis. Além disso, os tipos de matriz sempre foram covariant. Esses recursos existentes são diferentes dos novos de c# 4. 0, que efetivamente permitem definir seus próprios tipos de covariant e contravariant em alguns de seus parâmetros de tipo.

Expedição dinâmica

Para os recursos de interoperabilidade do c# 4. 0, começando com o que é talvez a maior alteração.

C# agora suporta vinculação posterior dinâmica. A linguagem sempre tem sido fortemente tipada e ele continua a ser isso na versão 4. 0. A Microsoft acredita que isso torna c# fáceis de usar, rápido e adequado para todo o trabalho de programadores .net são colocá-lo. Mas, há ocasiões em que você precisa se comunicar com sistemas não baseados em .net.

Tradicionalmente, havia pelo menos duas abordagens. A primeira era simplesmente importar o modelo externo diretamente para o .net como um proxy. Interoperabilidade COM fornece um exemplo. Desde o lançamento original do .NET Framework, ele usou essa estratégia com uma ferramenta chamada TLBIMP, que cria novos tipos de proxy do .net, que você pode usar diretamente da Linguagem c#.

LINQ para SQL, fornecido com o c# 3. 0, contém uma ferramenta chamada SQLMETAL, o que importa um banco de dados existente para classes de proxy c# para uso com consultas. Você também encontrará uma ferramenta importa as classes de WMI (Instrumentação de gerenciamento do Windows) para o c#. Muitas tecnologias permitem escrever c# (normalmente com atributos) e, em seguida, efetuar interop usando o seu código escritas à mão como base para ações externas, tais como o LINQ para SQL, o Windows Communication Foundation (WCF) e a serialização.

A segunda abordagem desfaz o sistema de tipos totalmente — você incorporar seqüências de caracteres e dados em seu código. Isso é o que fazer, sempre que você escreva código que, digamos, invoca um método em um objeto de JScript ou quando você incorpora uma consulta SQL em seu aplicativo ado.net. Você está fazendo isso, mesmo quando você adiar a ligação de runtime usando a reflexão, mesmo que a interoperabilidade nesse caso é com o .NET propriamente dito.

A palavra-chave dinâmica em c# é uma resposta para lidar com problemas de outras abordagens. Let’s começar com um exemplo simples — reflexão. Normalmente, usá-lo requer muitos códigos de infra-estrutura de texto padronizado, tais como:

object o = GetObject();
Type t = o.GetType();
object result = t.InvokeMember("MyMethod", 
  BindingFlags.InvokeMethod, null, 
  o, new object[] { });
int i = Convert.ToInt32(result);

Com a palavra-chave dinâmica, em vez de chamar um método MyMethod em algum objeto usando reflexão dessa maneira, você agora pode informar o compilador por favor, trate o como dinâmico e atrasar a toda a análise até o tempo de execução. Código que faz o que tem esta aparência:

dynamic o = GetObject();
int i = o.MyMethod();

Ele funciona, e ele realiza a mesma coisa com o código é muito menos complicada.

O valor dessa redução e simplificado c# sintaxe é, talvez mais desmarque esta opção se você olhar a classe de ScriptObject que ofereça suporte a operações em um objeto JScript. A classe tem um método InvokeMember que tenha mais e diferentes parâmetros, exceto no Silverlight, o que realmente tem um método Invoke (Observe a diferença no nome da) com menos parâmetros. Nem esses são os mesmos que você precise chamar um método em um objeto de IronPython ou IronRuby, ou em qualquer quantidade de não - c# podem chegar em contato com os objetos.

Juntamente com os objetos que vêm de linguagens dinâmicas, você encontrará uma variedade de modelos de dados que são inerentemente dinâmicos e possuem diferentes APIs com suporte para eles, como HTML DOMs, System. XML DOM e o modelo de XLinq para XML. Objetos COM freqüência são dinâmicos e podem se beneficiar com o atraso de tempo de algumas análises de compilador de execução.

Essencialmente, c# 4. 0 oferece um modo de exibição simples e consistente, de operações dinâmicas. Para tirar proveito dele, você só precisa fazer é especificar que um determinado valor dinâmico, garantindo que a análise de todas as operações no valor será atrasado até que o tempo de execução.

No c# 4. 0, dinâmico é um tipo interno e um pseudo-keyword especial significa que ele. Observe, no entanto, isto dinâmica é diferente da var. Variáveis declaradas com var realmente tem um tipo de alta segurança, mas o programador saiu para o compilador para descobrir. Quando o programador usa dinâmico, o compilador não sabe qual tipo está sendo usado, o programador deixa descobrir até o tempo de execução.

Dinâmico e o DLR

A infra-estrutura oferece suporte a essas operações dinâmicas em tempo de execução é chamada o DLR (Dynamic Language Runtime). Essa nova biblioteca do .NET Framework 4 é executado no CLR, como qualquer outra biblioteca gerenciada. Ele é responsável por cada operação dinâmica entre o idioma que iniciou a ele e o objeto que ele ocorre em corretagem. Se uma operação dinâmica não é tratada pelo objeto, em que ele ocorre, um componente de tempo de execução do compilador c# lida com o bind. Um diagrama de arquitetura simplificada e incompleta semelhante do Figura 1.

image: The DLR Runs on Top of the CLR

Figura 1 as execuções do DLR na parte superior do CLR

O interessante sobre uma operação dinâmica, como, por exemplo, uma chamada de método dinâmico, é que o objeto de destinatário tem a oportunidade de injetar-se a ligação em tempo de execução e completamente pode, determinar como resultado, a semântica de qualquer operação dinâmica especificada. Por exemplo, dê uma olhada no código a seguir:

dynamic d = new MyDynamicObject();
d.Bar("Baz", 3, d);

Se MyDynamicObject foi definido como mostrado aqui, você pode imaginar o que acontece:

class MyDynamicObject : DynamicObject {
  public override bool TryInvokeMember(
    InvokeMemberBinder binder, 
    object[] args, out object result) {

    Console.WriteLine("Method: {0}", binder.Name);
    foreach (var arg in args) {
      Console.WriteLine("Argument: {0}", arg);
    }

    result = args[0];
    return true;
  }
}

Na verdade, o código imprime:

Method: Bar
Argument: Baz
Argument: 3
Argument: MyDynamicObject

Declarando d ser do tipo dinâmico, o código que consome a instância MyDynamicObject efetivamente opta pelo check-out de verificação para as operações d participa em tempo de compilação. Usar de forma dinâmica “ Don sei qual tipo isso vai ser, portanto, Don sei quais métodos ou propriedades que existem no momento. Compilador, por favor, deixe-os, tudo isso por meio e, em seguida, descobrir quando você realmente tem um objeto em tempo de execução. ” Portanto, a chamada a barra é compilado, mesmo que o compilador não sabe o que significa. Em seguida, em tempo de execução, o próprio objeto é perguntado o que fazer com que esta chamada para barra. Esse é o que TryInvokeMember sabe como manipular.

Agora, suponha que, em vez de um MyDynamicObject você usou um objeto de Python:

dynamic d = GetPythonObject();
d.bar("Baz", 3, d);

Se o objeto no arquivo listado aqui, em seguida, o código também funciona e a saída é muito semelhante a:

def bar(*args):
  print "Method:", bar.__name__
  for x in args:
    print "Argument:", x

Nos bastidores para cada uso de um valor dinâmico, o compilador gera um monte de código que inicializa e usa um CallSite do DLR. Esse CallSite contém todas as informações necessárias para vincular em tempo de execução, incluindo itens como o nome do método dados extras, como, por exemplo, se a operação usa coloque em um contexto marcada e informações sobre os argumentos e seus tipos.

Esse código, se você tivesse mantê-lo, será cada bit feio, como o código de reflexão mostrado anteriormente ou o código de ScriptObject ou seqüências que contêm consultas XML. Esse é o problema do recurso dinâmico no c#, você Don precise escrever código como esse!

Ao usar a palavra-chave dinâmica, o código pode ser praticamente da maneira desejada: como uma invocação de método simples, uma chamada para um indexador, um operador, como +, uma projeção ou até mesmo compostos, como + = ou + +. Você pode até usar valores dinâmicas nas instruções — por exemplo, if(d) e foreach(var x in d). Short-circuiting também é suportado, com código como, por exemplo, d & & ShortCircuited ou d?? ShortCircuited.

O valor de ter o DLR fornece uma infra-estrutura comum para esses tipos de operações é que não tiver que lidar com uma API diferente para cada modelo dinâmico, você gostaria de código — há uma única API. E você nem precisa usá-lo. O compilador c# pode usá-lo para você e que deve dar mais tempo para ser realmente escrever o código desejado — o menos código de infra-estrutura precisa manter significa mais produtividade para você.

A linguagem c# não oferece nenhum atalho para a definição de objetos dinâmicos. Dinâmico no c# é demorada e usar objetos dinâmicos. Considere o seguinte:

dynamic list = GetDynamicList();
dynamic index1 = GetIndex1();
dynamic index2 = GetIndex2();
string s = list[++index1, index2 + 10].Foo();

Esse código é compilado e contém muitas operações dinâmicas. Em primeiro lugar, há o pre-increment dinâmico em index1, e o dinâmico adicione com index2. Em seguida, obtenha uma indexador dinâmico é chamada na lista. O produto dessas operações chama o membro Foo. Finalmente, o resultado total da expressão é convertido em uma seqüência de caracteres e armazenado em s. Cinco operações dinâmicas em uma única linha, cada expedidos em tempo de execução.

O tipo de tempo de compilação de cada operação dinâmico é dinâmico e, portanto, o tipo de “ dynamicness ” de fluxos de cálculo para cálculo. Mesmo se você não tivesse incluído várias vezes para expressões dinâmicos, ainda haveria um número de operações dinâmicas. Ainda há cinco nesta única linha:

string s = nonDynamicList[++index1, index2 + 10].Foo();

Como os resultados das duas expressões indexação são dinâmicos, o índice propriamente dito é bem. E como o resultado do índice é dinâmico, então, a chamada para Foo. Em seguida, está confrontado com a conversão de um valor dinâmico para uma seqüência de caracteres. Isso acontece dinamicamente, é claro que, porque o objeto pode ser um dinâmico que deseja executar alguns cálculo especial diante de uma solicitação de conversão.

Observe que o c# permite conversões implícitas de qualquer expressão dinâmica para qualquer tipo de exemplos anteriores. A cadeia de caracteres ao final da conversão é implícita e não exigia uma operação de conversão explícita. Da mesma forma, qualquer tipo pode ser convertido implicitamente em dinâmico.

Neste aspecto, dinâmico, é bem como o objeto e as semelhanças Don param por aí. Quando o compilador emite as suas necessidades para emitir uma variável dinâmica e de assembly, ele faz isso usando o objeto do tipo e, em seguida, marcando-a especialmente. Em algum sentido dinâmico é o tipo de um alias para o objeto, mas adiciona o comportamento extra de resolução dinâmica operações quando usá-lo.

Você pode ver isso, se você tentar converter entre tipos genéricos que diferem apenas em dinâmico e o objeto; tais conversões sempre funcionará, pois em tempo de execução, uma instância de lista <dynamic> é realmente uma instância de lista <object>:

List<dynamic> ld = new List<object>();

Você também pode ver a semelhança entre dinâmico e se você tentar substituir um método é declarado com um parâmetro de objeto do objeto:

class C {
  public override bool Equals(dynamic obj) { 
    /* ... */ 
  }
}

Embora ele resolve para um objeto decorado no seu assembly, gosto pensar dinâmico como um tipo real, pois ele funciona como um lembrete que você pode fazer a maioria das coisas com que você pode fazer com qualquer outro tipo. Você pode usá-lo como um argumento de tipo ou, digamos, como um valor de retorno. Por exemplo, esta definição de função permitirão que você usar o resultado da chamada de função dinamicamente sem ter que colocar o valor de retorno em uma variável dinâmica:

public dynamic GetDynamicThing() { 
  /* ... */ }

Há muito mais detalhes sobre o modo dinâmico é tratado e enviada, mas você Don precisa saber para usar o recurso. A idéia fundamental é que você pode escrever um código semelhante a c#, e se qualquer parte do código que você escreveu é dinâmico, o compilador irá deixar sozinho até o tempo de execução.

Desejo que abrange um tópico final referentes ao dinâmico: falha. Porque o compilador não pode verificar se o dinâmico que você está usando tem método denominado Foo, na verdade, ele não pode fornecer um erro. Obviamente, isso não significa que a chamada para Foo irá funcionar em tempo de execução. Ele pode funcionar, mas existem muitos objetos que Don têm um método denominado Foo. Quando a expressão não consegue ligar em tempo de execução, o fichário que faz a tentativa de melhor para lhe dar uma exceção que é mais ou menos exatamente o que o compilador poderia já disse se você não tivesse usado dinâmico começar com.

Considere o código a seguir:

try 
{
  dynamic d = "this is a string";
  d.Foo();
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
{
  Console.WriteLine(e.Message);
}

Aqui eu tenho uma seqüência de caracteres e as seqüências de caracteres com clareza não tem um método denominado Foo. Quando a linha que chama o Foo é executado, a ligação não terá êxito e você obterá uma RuntimeBinderException. Esse é o que imprime o programa anterior:

'string' does not contain a definition for 'Foo'

Qual é exatamente o erro da mensagem, como um programador de c#, esperado.

Parâmetros opcionais e de argumentos nomeados

Em outra adição à Linguagem c#, métodos agora oferecem suporte parâmetros opcionais com os valores padrão para que quando você chamar um método como você pode omitir os parâmetros. Você pode ver isso em ação nesta classe carro:

class Car {
  public void Accelerate(
    double speed, int? gear = null, 
    bool inReverse = false) { 

    /* ... */ 
  }
}

Você pode chamar o método desta forma:

Car myCar = new Car();
myCar.Accelerate(55);

Isso tem o mesmo efeito que:

myCar.Accelerate(55, null, false);

É o mesmo porque o compilador para inserir todos os valores padrão que você omita.

C# 4. 0 também permitirá chamar métodos especificando alguns argumentos por nome. Dessa forma, você pode passar um argumento para um parâmetro opcional sem precisar também passar argumentos para todos os parâmetros que vêm antes dele.

Digamos que você deseja chamar acelere ir na ordem inversa, mas você Don deseja especificar o parâmetro de trem de pouso. Bem, você pode fazer isso:

myCar.Accelerate(55, inReverse: true);

Esta é uma nova sintaxe c# 4. 0 e é o mesmo como se você tivesse escrito:

myCar.Accelerate(55, null, true);

Na verdade, independentemente de serem ou não parâmetros no método que você está chamando são opcionais, você pode usar nomes ao passar argumentos. Por exemplo, essas duas chamadas são permitidas e idêntico uns aos outros:

Console.WriteLine(format: "{0:f}", arg0: 6.02214179e23);
Console.WriteLine(arg0: 6.02214179e23, format: "{0:f}");

Se você estiver chamando um método que usa uma longa lista de parâmetros, você pode até mesmo usar nomes como uma espécie de documentação no código para ajudá-lo a lembrar-se de que o parâmetro é que.

Superficialmente, argumentos opcionais parâmetros nomeados Don aparência e os recursos de interoperabilidade. Você pode usá-los sem nunca mesmo pensar sobre a interoperabilidade. No entanto, a motivação para esses recursos proveniente de APIs do Office. Considere, por exemplo, o Word programação e algo simples, como o método SaveAs na interface de documentos. Esse método tem 16 parâmetros, os quais são opcionais. Nas versões anteriores do c#, se você deseja chamar esse método tem que escrever código que tem esta aparência:

Document d = new Document();
object filename = "Foo.docx";
object missing = Type.Missing;
d.SaveAs(ref filename, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
 ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
 ref missing, ref missing);

Agora, você pode escrever isso:

Document d = new Document();
d.SaveAs(FileName: "Foo.docx");

Eu diria que é um aperfeiçoamento para qualquer pessoa que funciona com as APIs da seguinte maneira. E melhorar a vida dos programadores que precisam para escrever programas do Office foi definitivamente um fator para a adição de motivando nomeado argumentos e os parâmetros opcionais para o idioma.

Agora, quando escrever uma biblioteca .net e considerar a adição de métodos que possuem parâmetros opcionais, você se depara com uma opção. Você pode adicionar parâmetros opcionais ou você pode fazer por anos fizeram programadores de c#: apresente as sobrecargas. No exemplo Car.Accelerate, a última decisão pode levá-lo para produzir um tipo que tem esta aparência:

class Car {
  public void Accelerate(uint speed) { 
    Accelerate(speed, null, false); 
  }
  public void Accelerate(uint speed, int? gear) { 
    Accelerate(speed, gear, false); 
  }
  public void Accelerate(uint speed, int? gear, 
    bool inReverse) { 
    /* ... */ 
  }
}

Selecionar o modelo mais adequado a biblioteca que você esteja escrevendo depende de você. Porque a Linguagem c# ainda não tinha parâmetros opcionais até agora, o .NET Framework (incluindo o .NET Framework 4) tende a usar sobrecargas. Se você decidir misturar e combinar sobrecargas com parâmetros opcionais, a resolução de sobrecarga do c# tem regras claras desempate para determinar qual sobrecarga para chamar em nenhuma circunstância determinada.

Propriedades indexadas

Alguns recursos de linguagem menores no c# 4. 0 têm suporte apenas ao escrever código em relação a uma API de interoperabilidade COM. A interoperabilidade na ilustração anterior do Word é um exemplo.

O código c# sempre teve a noção de um indexador que você pode adicionar a uma classe para efetivamente sobrecarregar o operador de [] em instâncias dessa classe. Nesse sentido do indexador também é chamado de um indexador padrão, desde que ele não for fornecido um nome e a chamá-lo requer sem nome. Algumas APIs de COM também têm indexadores não são padrão, o que quer dizer que você não é possível chamá-los com eficiência usando simplesmente o [], você deve especificar um nome. Como alternativa, você pode considerar uma propriedade indexada como uma propriedade que leva alguns argumentos extras.

C# 4. 0 oferece suporte a propriedades indexadas em tipos de interoperabilidade COM. Não é possível definir tipos em c# que tenham propriedades de indexados, mas você pode usá-los, desde que você está fazendo assim por diante um COM tipo. Para obter um exemplo da aparência de código em c# que faz isso, considere a propriedade Range em uma planilha do Excel:

using Microsoft.Office.Interop.Excel;

class Program {
  static void Main(string[] args) {
    Application excel = new Application();
    excel.Visible = true;

    Worksheet ws = 
      excel.Workbooks.Add().Worksheets["Sheet1"];
    // Range is an indexed property
    ws.Range["A1", "C3"].Value = 123; 
    System.Console.ReadLine();
    excel.Quit();
  }
}

Neste exemplo, o intervalo [“ A1 ”, “ C3 ”] não é uma propriedade chamada Range retorna uma coisa que possa ser indexada. É uma chamada para um acessador de intervalo passa A1 e C3 com ele. E embora o valor não pode parecer uma propriedade indexada, também, é um! Todos os argumentos são opcionais e como ele é uma propriedade indexada, omiti-los, não especificando-os em todos os. Antes que o idioma com suporte indexado propriedades, a chamada como este poderia ter escrito:

ws.get_Range("A1", "C3").Value2 = 123;

Aqui, valor2 é uma propriedade que tenha sido adicionada, simplesmente porque a propriedade indexada valor funcionariam anteriores à 4. 0 do c#.

Omitindo a palavra-chave ref nos sites de chamada de COM

Algumas APIs COM foram escritas com muitos parâmetros passados por referência, mesmo quando a implementação não gravar de volta para eles. No conjunto do Office, o Word se destaca por exemplo, todas as suas APIs COM isso.

Quando estiver ser confrontado com uma biblioteca e você precisa passar argumentos por referência, você não pode passar qualquer expressão que não seja um campo ou variável local e que é uma grande dor de cabeça. O exemplo de salvar como do Word, você pode ver isso em ação — era que declarar um local chamado de nome de arquivo e um local chamado faltando apenas para chamar o método SaveAs, já que esses parâmetros são necessários a serem passados por referência.

Document d = new Document();
object filename = "Foo.docx";
object missing = Type.Missing;
d.SaveAs(ref filename, ref missing, // ...

Talvez você tenha observado no novo código c# que seguiu, declarei não há mais de um local para o nome do arquivo:

d.SaveAs(FileName: "Foo.docx");

Isso é possível devido a novos omitir recurso ref for COM interop. Agora, ao chamar um método COM interop, você pode passar um argumento por valor, em vez de por referência. Se fizer isso, o compilador irá criar um local temporário em seu nome e passe que local por referência para que você se necessário. É claro, você não poderá ver o efeito da chamada do método se o método mutates o argumento — se desejar, passar o argumento por ref.

Isso deve fazer o código que usa as APIs como este muito cartucho de limpeza.

A incorporação de tipos de interoperabilidade de COM

Esta é a mais de um recurso do compilador c# que um recurso de linguagem, mas agora você pode usar um assembly de interoperabilidade de COM sem esse assembly ter de estar presente em tempo de execução. O objetivo é reduzir a carga da implantação de assemblies de interoperabilidade COM o seu aplicativo.

Quando a interoperabilidade COM foi introduzida na versão original do .NET Framework, a noção de um assembly de interoperabilidade primária (PIA) foi criada. Essa foi uma tentativa de resolver o problema de compartilhamento de objetos COM entre componentes. Se você tivesse diferentes assemblies de interoperabilidade que definiu uma planilha do Excel, não seria capaz de compartilhar essas planilhas entre componentes, porque elas seriam diferentes tipos de .net. O PIA corrigidos isso existentes de uma só vez — todos os clientes usá-lo, e os tipos .net sempre coincidem.

Embora uma excelente idéia no papel, na prática Implantando que um PIA é uma dor de cabeça, porque só há um e vários aplicativos pode tentar instalar ou desinstalá-lo. Assuntos são complicados porque os PIAs são geralmente grandes, Office não implantá-los com instalações padrão do Office e os usuários podem contornar esse sistema único assembly com facilidade apenas usando TLBIMP para criar seu próprio assembly de interoperabilidade.

Portanto, agora, uma tentativa de corrigir essa situação, duas coisas tem acontecido:

  • O tempo de execução, foi dada smarts tratar dois estruturalmente idênticos COM interop tipos que compartilham as mesmas características de identificação (nome, GUID e assim por diante) como se estivessem realmente do mesmo tipo .net.
  • O compilador c# aproveita disso, reproduzir apenas os tipos de interoperabilidade no seu próprio assembly quando você compila, eliminando a necessidade do assembly de interoperabilidade existirem em tempo de execução.

Eu tenho que omitir alguns detalhes de espaço, mas mesmo sem o conhecimento dos detalhes, este é outro recurso — como dinâmico — você poderá utilizar sem problemas. Você informa o compilador para incorporar tipos de interoperabilidade para você no Visual Studio, definindo a propriedade de incorporar tipos de interoperabilidade em sua referência para true.

Porque a equipe do c# espera que esse é o método preferido para referenciar os módulos COM, o Visual Studio definir essa propriedade como True por padrão para qualquer nova referência de interop adicionada a um projeto c#. Se você estiver usando o compilador de linha de comando (exe) para compilar seu código, em seguida, para incorporar a interoperabilidade tipos, você deve fazer referência ao assembly de interoperabilidade em questão usar o /L alternar em vez de /R.

Cada um dos recursos que abordei neste artigo pode se gerar discussão muito mais, e todos os tópicos merecem artigos seus próprios. Eu tiver omitido ou glossed em muitos detalhes, mas espero que isso serve como um bom ponto de partida para explorar o c# 4. 0 e você encontrar tempo para investigar e fazer usar esses recursos. E se você fizer isso, espero que aproveite os benefícios de produtividade e a legibilidade do programa que foram projetadas para dar a você.

Chris Burrows é um desenvolvedor na Microsoft da Linguagem c# compilador de equipe. Ele implementou dinâmico em que o compilador c# e está envolvido com o desenvolvimento do Visual Studio para nove anos.

Meus agradecimentos aos seguinte especialista técnico para revisar este artigo: Eric Lippert