O C # 4.0 introduziu um novo tipo chamado 'dinâmico'. Tudo parece bom, mas para que um programador o usaria?
Existe uma situação em que ele pode salvar o dia?
O C # 4.0 introduziu um novo tipo chamado 'dinâmico'. Tudo parece bom, mas para que um programador o usaria?
Existe uma situação em que ele pode salvar o dia?
Respostas:
A palavra-chave dinâmica é nova no C # 4.0 e é usada para informar ao compilador que o tipo de uma variável pode ser alterado ou que não é conhecido até o tempo de execução. Pense nisso como sendo capaz de interagir com um Objeto sem precisar lançá-lo.
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
Observe que não precisamos lançar nem declarar cust como tipo Cliente. Como declaramos dinâmico, o tempo de execução assume o controle e, em seguida, pesquisa e define a propriedade FirstName para nós. Agora, é claro, quando você está usando uma variável dinâmica, está desistindo da verificação do tipo de compilador. Isso significa que a chamada cust.MissingMethod () será compilada e não falhará até o tempo de execução. O resultado dessa operação é uma RuntimeBinderException porque MissingMethod não está definido na classe Customer.
O exemplo acima mostra como a dinâmica funciona ao chamar métodos e propriedades. Outro recurso poderoso (e potencialmente perigoso) é poder reutilizar variáveis para diferentes tipos de dados. Tenho certeza de que os programadores Python, Ruby e Perl por aí podem pensar em um milhão de maneiras de tirar proveito disso, mas uso o C # há tanto tempo que parece "errado" para mim.
dynamic foo = 123;
foo = "bar";
OK, então você provavelmente não escreverá código como o acima com muita frequência. Pode haver momentos, no entanto, em que a reutilização de variáveis pode ser útil ou limpar um pedaço sujo de código legado. Um caso simples em que me deparo frequentemente é a necessidade constante de converter entre decimal e duplo.
decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");
A segunda linha não é compilada porque 2,5 é digitado como duplo e a linha 3 não é compilada porque Math.Sqrt espera um duplo. Obviamente, tudo o que você precisa fazer é converter e / ou alterar seu tipo de variável, mas pode haver situações em que a dinâmica faça sentido.
dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");
Leia mais sobre o recurso: http://www.codeproject.com/KB/cs/CSharp4Features.aspx
dynamic
in c # para resolver problemas que podem ser resolvidos (talvez até melhor) por recursos padrão de c # e digitação estática, ou no máximo com inferência de tipo ( var
). sódynamic
deve ser usado quando se trata de problemas de interoperabilidade com o DLR. Se você escrever código em uma linguagem estática, como c # is, faça-o e não emule uma linguagem dinâmica. Isso é apenas feio.
dynamic
variáveis em seu código onde não precisa delas (como no exemplo com o squareroot), desiste da verificação de erros em tempo de compilação; agora você está recebendo possíveis erros de tempo de execução.
A dynamic
palavra-chave foi adicionada, juntamente com muitos outros novos recursos do C # 4.0, para facilitar a conversação com código que vive ou provém de outros tempos de execução, que possui APIs diferentes.
Veja um exemplo.
Se você possui um objeto COM, como o Word.Application
objeto, e deseja abrir um documento, o método para fazer isso é fornecido com pelo menos 15 parâmetros, a maioria dos quais é opcional.
Para chamar esse método, você precisaria de algo assim (estou simplificando, esse não é um código real):
object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing);
Observe todos esses argumentos? Você precisa passar isso desde o C # antes da versão 4.0 não ter noção de argumentos opcionais. No C # 4.0, as APIs do COM foram facilitadas de trabalhar, apresentando:
ref
opcional para APIs COMA nova sintaxe para a chamada acima seria:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
Veja como fica mais fácil, mais legível se torna?
Vamos separar isso:
named argument, can skip the rest
|
v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
^ ^
| |
notice no ref keyword, can pass
actual parameter values instead
A mágica é que o compilador C # agora injeta o código necessário e trabalha com novas classes no tempo de execução, para fazer quase exatamente a mesma coisa que você fez antes, mas a sintaxe foi oculta para você, agora você pode se concentrar no o que , e não tanto sobre como . Anders Hejlsberg gosta de dizer que você precisa invocar diferentes "encantamentos", que são uma espécie de trocadilho com a magia da coisa toda, onde você normalmente tem que agitar as mãos e dizer algumas palavras mágicas na ordem certa para conseguir um certo tipo de feitiço. A maneira antiga da API de conversar com objetos COM era muito disso; era necessário passar por muitos obstáculos para convencer o compilador a compilar o código para você.
As coisas se decompõem em C # antes da versão 4.0 ainda mais se você tentar falar com um objeto COM para o qual você não tem uma interface ou classe, tudo que você tem é uma IDispatch
referência.
Se você não sabe o que é, IDispatch
é basicamente uma reflexão para objetos COM. Com uma IDispatch
interface, você pode perguntar ao objeto "qual é o número de identificação do método conhecido como Salvar" e criar matrizes de um determinado tipo que contém os valores do argumento e, finalmente, chamar um Invoke
método na IDispatch
interface para chamar o método, passando tudo as informações que você conseguiu reunir juntos.
O método Save acima pode se parecer com este (definitivamente não é o código certo):
string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);
Tudo isso por apenas abrir um documento.
O VB tinha argumentos opcionais e suporte para a maioria disso fora da caixa há muito tempo, portanto, este código C #:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
é basicamente apenas C # alcançando VB em termos de expressividade, mas fazendo isso da maneira certa, tornando-o extensível, e não apenas para COM. Obviamente, isso também está disponível para o VB.NET ou qualquer outro idioma criado sobre o tempo de execução do .NET.
Você pode encontrar mais informações sobre a IDispatch
interface na Wikipedia: IDispatch, se quiser ler mais sobre ela. É uma coisa realmente sangrenta.
No entanto, e se você quisesse conversar com um objeto Python? Existe uma API diferente daquela usada para objetos COM e, como os objetos Python também são dinâmicos por natureza, você precisa recorrer à magia de reflexão para encontrar os métodos certos para chamar, seus parâmetros, etc., mas não o .NET reflexão, algo escrito para Python, praticamente como o código IDispatch acima, completamente diferente.
E para Ruby? Uma API diferente ainda.
JavaScript? O mesmo negócio, API diferente para isso também.
A palavra-chave dinâmica consiste em duas coisas:
dynamic
dynamic
palavra - chave e mapeia as chamadas para a maneira correta de fazer as coisas. A API está até documentada; portanto, se você tiver objetos provenientes de um tempo de execução não coberto, poderá adicioná-lo.A dynamic
palavra-chave não se destina, no entanto, a substituir qualquer código existente somente do .NET. Claro, você pode fazê-lo, mas não foi adicionado por esse motivo, e os autores da linguagem de programação C # com Anders Hejlsberg na frente foram os mais inflexíveis de que ainda consideram o C # como uma linguagem fortemente tipada e não sacrificam esse princípio.
Isso significa que, embora você possa escrever um código como este:
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
e compilá-lo, não era para ser uma espécie de sistema de mágica que permite descobrir o que você quis dizer em tempo de execução.
O objetivo era facilitar a conversa com outros tipos de objetos.
Há muito material na internet sobre a palavra-chave, proponentes, oponentes, discussões, reclamações, elogios etc.
Sugiro que você comece com os seguintes links e depois pesquise no Google:
dynamic
foi adicionado, para apoiar outros ecossistemas sobre como a invocação de método semelhante à reflexão pode ser feita, além de fornecer uma espécie de abordagem de caixa preta para estruturas de dados com uma maneira documentada de conseguir isso.
Estou surpreso que ninguém tenha mencionado vários despachos . A maneira usual de contornar isso é através do padrão Visitor e isso nem sempre é possível, então você acaba com is
verificações empilhadas .
Então, aqui está um exemplo da vida real de uma aplicação minha. Em vez de fazer:
public static MapDtoBase CreateDto(ChartItem item)
{
if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
//other subtypes follow
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
Você faz:
public static MapDtoBase CreateDto(ChartItem item)
{
return CreateDtoImpl(item as dynamic);
}
private static MapDtoBase CreateDtoImpl(ChartItem item)
{
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
private static MapDtoBase CreateDtoImpl(MapPoint item)
{
return new MapPointDto(item);
}
private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
return new ElevationDto(item);
}
Observe que, no primeiro caso, ElevationPoint
é uma subclasse de MapPoint
e, se não for colocada antes MapPoint
, nunca será alcançada. Esse não é o caso da dinâmica, pois o método de correspondência mais próximo será chamado.
Como você pode imaginar, a partir do código, esse recurso foi útil enquanto eu realizava a tradução dos objetos ChartItem para suas versões serializáveis. Eu não queria poluir meu código com os visitantes e também não queria poluir meus ChartItem
objetos com atributos específicos de serialização inútil.
is
empilhamento um em cima do outro.
magic
; não existe mágica.
Isso facilita a interoperabilidade entre as linguagens estáticas de tipo estáticas (CLR) e as dinâmicas (python, ruby ...) em execução no DLR (Dynamic Language Runtime), consulte MSDN :
Por exemplo, você pode usar o código a seguir para incrementar um contador em XML em C #.
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
Usando o DLR, você poderia usar o seguinte código para a mesma operação.
scriptobj.Count += 1;
O MSDN lista estas vantagens:
- Simplifica a portabilidade de idiomas dinâmicos para o .NET Framework
- Habilita os recursos dinâmicos nos idiomas de tipo estaticamente
- Fornece benefícios futuros do DLR e .NET Framework
- Permite o compartilhamento de bibliotecas e objetos
- Fornece expedição dinâmica rápida e invocação
Veja MSDN para mais detalhes.
Um exemplo de uso:
Você consome muitas classes que possuem uma propriedade comum 'CreationDate':
public class Contact
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Company
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Opportunity
{
// some properties
public DateTime CreationDate { get; set; }
}
Se você escrever um método commun que recupere o valor da propriedade 'CreationDate', precisará usar a reflexão:
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
Com o conceito 'dinâmico', seu código é muito mais elegante:
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
Ele será usado principalmente pelas vítimas de RAD e Python para destruir a qualidade do código, o IntelliSense e a detecção de erros em tempo de compilação.
Ele é avaliado em tempo de execução, para que você possa mudar o tipo como em JavaScript para o que quiser. Isso é legítimo:
dynamic i = 12;
i = "text";
E assim você pode alterar o tipo conforme necessário. Use-o como último recurso; é benéfico, mas ouvi muita coisa acontecendo nos bastidores em termos de IL gerada e que pode ter um preço de desempenho.
O melhor caso de uso de variáveis do tipo 'dinâmico' para mim foi quando, recentemente, eu estava escrevendo uma camada de acesso a dados no ADO.NET ( usando SQLDataReader ) e o código estava invocando os procedimentos armazenados herdados já gravados. Existem centenas desses procedimentos armazenados herdados que contêm grande parte da lógica de negócios. Minha camada de acesso a dados precisava retornar algum tipo de dado estruturado à camada de lógica de negócios, baseada em C #, para fazer algumas manipulações ( embora quase não existam ). Todos os procedimentos armazenados retornam um conjunto diferente de dados ( colunas da tabela ). Então, em vez de criar dezenas de classes ou estruturas para armazenar os dados retornados e passá-los para o BLL, escrevi o código abaixo, que parece bastante elegante e arrumado.
public static dynamic GetSomeData(ParameterDTO dto)
{
dynamic result = null;
string SPName = "a_legacy_stored_procedure";
using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
{
SqlCommand command = new SqlCommand(SPName, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
dynamic row = new ExpandoObject();
row.EmpName = reader["EmpFullName"].ToString();
row.DeptName = reader["DeptName"].ToString();
row.AnotherColumn = reader["AnotherColumn"].ToString();
result = row;
}
}
}
return result;
}
dynamic np = Py.Import("numpy")
dynamic
aplicar operadores numéricos neles. Isso fornece segurança de tipo e evita limitações de genéricos. Isso é essencialmente * digitação de pato:T y = x * (dynamic)x
, Onde typeof(x) is T
Outro caso de uso para dynamic
digitação é para métodos virtuais que enfrentam um problema de covariância ou contravariância. Um exemplo é o Clone
método infame que retorna um objeto do mesmo tipo que o objeto em que é chamado. Esse problema não é completamente resolvido com um retorno dinâmico, porque ignora a verificação de tipo estático, mas pelo menos você não precisa usar projeções feias o tempo todo, conforme o uso simples object
. Caso contrário, para dizer, os elencos se tornam implícitos.
public class A
{
// attributes and constructor here
public virtual dynamic Clone()
{
var clone = new A();
// Do more cloning stuff here
return clone;
}
}
public class B : A
{
// more attributes and constructor here
public override dynamic Clone()
{
var clone = new B();
// Do more cloning stuff here
return clone;
}
}
public class Program
{
public static void Main()
{
A a = new A().Clone(); // No cast needed here
B b = new B().Clone(); // and here
// do more stuff with a and b
}
}