Risposte:
La dynamic
parola chiave viene utilizzata per dichiarare variabili che devono essere associate in ritardo.
Se si desidera utilizzare l'associazione tardiva, per qualsiasi tipo reale o immaginario, si utilizza la dynamic
parola chiave e il compilatore fa il resto.
Quando si utilizza la dynamic
parola chiave per interagire con un'istanza normale, il DLR esegue chiamate associate in ritardo ai metodi normali dell'istanza.
L' IDynamicMetaObjectProvider
interfaccia consente a una classe di assumere il controllo del suo comportamento in ritardo.
Quando si utilizza la dynamic
parola chiave per interagire con IDynamicMetaObjectProvider
un'implementazione, il DLR chiama i IDynamicMetaObjectProvider
metodi e l'oggetto stesso decide cosa fare.
Le classi ExpandoObject
e DynamicObject
sono implementazioni di IDynamicMetaObjectProvider
.
ExpandoObject
è una classe semplice che ti consente di aggiungere membri a un'istanza e usarli dynamic
alleati.
DynamicObject
è un'implementazione più avanzata che può essere ereditata per fornire facilmente comportamenti personalizzati.
Cercherò di fornire una risposta più chiara a questa domanda, per spiegare chiaramente quali sono le differenze tra dinamico ExpandoObject
e DynamicObject
.
Molto rapidamente, dynamic
è una parola chiave. Non è un tipo di per sé. È una parola chiave che dice al compilatore di ignorare il controllo del tipo statico in fase di progettazione e di utilizzare invece l'associazione tardiva in fase di esecuzione. Quindi non passeremo molto tempo dynamic
nel resto di questa risposta.
ExpandoObject
e DynamicObject
sono davvero tipi. Sulla SUPERFICIE, sembrano molto simili tra loro. Entrambe le classi implementano IDynamicMetaObjectProvider
. Tuttavia, scava più a fondo e scoprirai che NON sono affatto simili.
DynamicObject è un'implementazione parziale del IDynamicMetaObjectProvider
puro scopo di essere un punto di partenza per gli sviluppatori di implementare i propri tipi personalizzati che supportano l'invio dinamico con l'archiviazione sottostante personalizzata e il comportamento di recupero per far funzionare l'invio dinamico.
In breve, utilizzare DynamicObject quando si desidera creare i propri tipi PROPRI che possono essere utilizzati con il DLR e lavorare con qualsiasi comportamento CUSTOM desiderato.
Esempio: immagina di voler avere un tipo dinamico che restituisca un valore predefinito personalizzato ogni volta che si tenta di ottenere un membro su un membro che NON esiste (ovvero che non è stato aggiunto in fase di esecuzione). E quel default dirà "Mi dispiace, non ci sono biscotti in questo vaso!". Se vuoi un oggetto dinamico che si comporti in questo modo, dovrai controllare cosa succede quando non viene trovato un campo. ExpandoObject non ti consente di farlo. Quindi dovrai creare il tuo tipo con un comportamento unico di risoluzione (invio) dei membri dinamici e usarlo al posto di quello già pronto ExpandoObject
.
È possibile creare un tipo come segue: (Nota, il codice seguente è solo a scopo illustrativo e potrebbe non essere eseguito. Per informazioni su come utilizzare correttamente DynamicObject, ci sono molti articoli e tutorial altrove.)
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;
}
}
Ora, potremmo usare questa classe immaginaria che abbiamo appena creato come un tipo dinamico che ha un comportamento molto personalizzato se il campo non esiste.
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
è un'implementazione COMPLETA di IDynamicMetaObjectProvider
, in cui il team di .NET Framework ha preso tutte queste decisioni per te. Questo è utile se non hai bisogno di alcun comportamento personalizzato e ritieni che ExpandoObject funzioni abbastanza bene per te (il 90% delle volte ExpandoObject
è abbastanza buono). Ad esempio, vedere quanto segue e che per ExpandoObject, i progettisti hanno scelto di generare un'eccezione se il membro dinamico non esiste.
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) { ... }
Quindi, per riassumere, ExpandoObject
è semplicemente un modo prescelto per estendere DynamicObject con determinati comportamenti di spedizione dinamica che probabilmente funzioneranno per te , ma potrebbero non dipendere dalle tue esigenze particolari.
Considerando che, DyanmicObject
è un Helper BaseType che rende semplice e facile implementare i propri tipi con comportamenti dinamici unici.
Un tutorial utile su cui si basa gran parte della fonte di esempio sopra.
DynamicObject
: quando TryGetMember
si RuntimeBinderException
esegue l' override , se si restituisce false a verrà generata quando si tenta di accedere a proprietà inesistenti. Affinché lo snippet funzioni effettivamente dovresti tornare true
.
Secondo la specifica del linguaggio C # dynamic
è una dichiarazione di tipo. Vale a dynamic x
dire che la variabile x
ha il tipo dynamic
.
DynamicObject
è un tipo che semplifica l'implementazione IDynamicMetaObjectProvider
e quindi sovrascrive il comportamento di associazione specifico per il tipo.
ExpandoObject
è un tipo che si comporta come una borsa di proprietà. Vale a dire è possibile aggiungere proprietà, metodi e così via alle istanze dinamiche di questo tipo in fase di esecuzione.
dynamic
non è un tipo reale ... è solo un suggerimento per dire al compilatore di usare l'associazione tardiva per questa variabile. dynamic
le variabili sono effettivamente dichiarate come object
in MSIL
L'esempio di cui sopra DynamicObject
non dice chiaramente la differenza, perché fondamentalmente implementa la funzionalità già fornita da ExpandoObject
.
Nei due collegamenti menzionati di seguito, è molto chiaro che, con l'aiuto di DynamicObject
, è possibile conservare / modificare il tipo effettivo ( XElement
nell'esempio utilizzato nei collegamenti seguenti) e un migliore controllo su proprietà e metodi.
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;
}
}
}