Respostas:
A dynamic
palavra-chave é usada para declarar variáveis que devem ser atrasadas.
Se você deseja usar a ligação tardia, para qualquer tipo real ou imaginário, use odynamic
palavra chave e o compilador fará o resto.
Quando você usa a dynamic
palavra-chave para interagir com uma instância normal, o DLR executa chamadas com ligação tardia aos métodos normais da instância.
A IDynamicMetaObjectProvider
interface permite que uma classe assuma o controle de seu comportamento de ligação tardia.
Quando você usa a dynamic
palavra-chave para interagir com uma IDynamicMetaObjectProvider
implementação, o DLR chama os IDynamicMetaObjectProvider
métodos e o próprio objeto decide o que fazer.
As classes ExpandoObject
e DynamicObject
são implementações de IDynamicMetaObjectProvider
.
ExpandoObject
é uma classe simples que permite adicionar membros a uma instância e usá-los como dynamic
aliados.
DynamicObject
é uma implementação mais avançada que pode ser herdada para fornecer facilmente um comportamento personalizado.
Tentarei fornecer uma resposta mais clara a essa pergunta, para explicar claramente quais são as diferenças entre dinâmico ExpandoObject
e DynamicObject
.
Muito rapidamente, dynamic
é uma palavra-chave. Não é um tipo per se. É uma palavra-chave que instrui o compilador a ignorar a verificação de tipo estático no tempo de design e, em vez disso, usar a ligação tardia no tempo de execução. Portanto, não vamos gastar muito tempodynamic
restante desta resposta.
ExpandoObject
e DynamicObject
são de fato tipos. Na SUPERFÍCIE, eles se parecem muito. Ambas as classes são implementadas IDynamicMetaObjectProvider
. No entanto, se aprofundar e você verá que eles não são semelhantes.
O DynamicObject é uma implementação parcial do IDynamicMetaObjectProvider
objetivo puramente de ser o ponto de partida para os desenvolvedores implementarem seus próprios tipos personalizados que suportam o envio dinâmico com comportamento de recuperação e armazenamento subjacente personalizado para fazer o envio dinâmico funcionar.
Em resumo, use DynamicObject quando desejar criar seus próprios tipos que podem ser usados com o DLR e trabalhar com qualquer comportamento PERSONALIZADO que você desejar.
Exemplo: imagine que você gostaria de ter um tipo dinâmico que retorne um padrão personalizado sempre que uma tentativa de obtenção de um membro que NÃO existe (ou seja, não foi adicionada em tempo de execução). E esse padrão dirá: "Sinto muito, não há cookies neste frasco!". Se você deseja um objeto dinâmico que se comporte dessa maneira, precisará controlar o que acontece quando um campo não é encontrado. ExpandoObject não permitirá que você faça isso. Portanto, você precisará criar seu próprio tipo com um comportamento exclusivo de resolução dinâmica de despacho (despacho) e usá-lo em vez do software prontoExpandoObject
.
Você pode criar um tipo da seguinte maneira: (Observe que o código abaixo é apenas ilustrativo e pode não ser executado. Para saber como usar o DynamicObject corretamente, existem muitos artigos e tutoriais em outros lugares.)
public class MyNoCookiesInTheJarDynamicObject : DynamicObject
{
Dictionary<string, object> properties = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (properties.ContainsKey(binder.Name))
{
result = properties[binder.Name];
return true;
}
else
{
result = "I'm sorry, there are no cookies in this jar!"; //<-- THIS IS OUR
CUSTOM "NO COOKIES IN THE JAR" RESPONSE FROM OUR DYNAMIC TYPE WHEN AN UNKNOWN FIELD IS ACCESSED
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
properties[binder.Name] = value;
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
dynamic method = properties[binder.Name];
result = method(args[0].ToString(), args[1].ToString());
return true;
}
}
Agora, poderíamos usar essa classe imaginária que acabamos de criar como um tipo dinâmico que possui um comportamento muito personalizado se o campo não existir.
dynamic d = new MyNoCookiesInTheJarDynamicObject();
var s = d.FieldThatDoesntExist;
//in our contrived example, the below should evaluate to true
Assert.IsTrue(s == "I'm sorry, there are no cookies in this jar!")
ExpandoObject
é uma implementação COMPLETA de IDynamicMetaObjectProvider
, onde a equipe do .NET Framework tomou todas essas decisões por você. Isso é útil se você não precisar de nenhum comportamento personalizado e acha que o ExpandoObject funciona bem o suficiente para você (90% do tempo, ExpandoObject
é bom o suficiente). Por exemplo, veja o seguinte e, para o ExpandoObject, os designers optaram por lançar uma exceção se o membro dinâmico não existir.
dynamic d = new ExpandoObject();
/*
The ExpandoObject designers chose that this operation should result in an
Exception. They did not have to make that choice, null could
have been returned, for example; or the designers could've returned a "sorry no cookies in the jar" response like in our custom class. However, if you choose to use
ExpandoObject, you have chosen to go with their particular implementation
of DynamicObject behavior.
*/
try {
var s = d.FieldThatDoesntExist;
}
catch(RuntimeBinderException) { ... }
Então, para resumir, ExpandoObject
é simplesmente uma maneira pré-escolhida de estender o DynamicObject com certos comportamentos de despacho dinâmico que provavelmente funcionarão para você , mas podem não depender de suas necessidades particulares.
Considerando que, DyanmicObject
é um BaseType auxiliar que simplifica e facilita a implementação de seus próprios tipos com comportamentos dinâmicos exclusivos.
Um tutorial útil no qual grande parte da fonte de exemplo acima é baseada.
DynamicObject
: ao substituir TryGetMember
, se você retornar falso, a RuntimeBinderException
será lançada ao tentar acessar a propriedade não existente. Para que o snippet funcione, você deve retornar true
.
De acordo com a especificação da linguagem C #, dynamic
é uma declaração de tipo. Ou seja, dynamic x
significa que a variável x
tem o tipodynamic
.
DynamicObject
é um tipo que facilita a implementação IDynamicMetaObjectProvider
e, portanto, substitui o comportamento de ligação específico para o tipo.
ExpandoObject
é um tipo que age como uma bolsa de propriedade. Ou seja, você pode adicionar propriedades, métodos e assim por diante a instâncias dinâmicas desse tipo em tempo de execução.
dynamic
não é um tipo real ... é apenas uma dica para dizer ao compilador para usar a ligação tardia para essa variável. dynamic
variáveis são realmente declarado como object
em MSIL
O exemplo acima DynamicObject
não indica claramente a diferença, porque está basicamente implementando a funcionalidade que já é fornecida peloExpandoObject
.
Nos dois links mencionados abaixo, é muito claro que, com a ajuda de DynamicObject
, é possível preservar / alterar o tipo real ( XElement
no exemplo usado nos links abaixo) e melhor controle sobre propriedades e métodos.
public class DynamicXMLNode : DynamicObject
{
XElement node;
public DynamicXMLNode(XElement node)
{
this.node = node;
}
public DynamicXMLNode()
{
}
public DynamicXMLNode(String name)
{
node = new XElement(name);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
XElement setNode = node.Element(binder.Name);
if (setNode != null)
setNode.SetValue(value);
else
{
if (value.GetType() == typeof(DynamicXMLNode))
node.Add(new XElement(binder.Name));
else
node.Add(new XElement(binder.Name, value));
}
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
XElement getNode = node.Element(binder.Name);
if (getNode != null)
{
result = new DynamicXMLNode(getNode);
return true;
}
else
{
result = null;
return false;
}
}
}