Odpowiedzi:
Słowo dynamic
kluczowe służy do deklarowania zmiennych, które powinny być wiązane późno.
Jeśli chcesz użyć późnego wiązania, dla dowolnego typu rzeczywistego lub wyobrażonego, użyj dynamic
słowa kluczowego, a kompilator zajmie się resztą.
Gdy używasz dynamic
słowa kluczowego do interakcji z normalnym wystąpieniem, DLR wykonuje wywołania z późnym wiązaniem do normalnych metod wystąpienia.
IDynamicMetaObjectProvider
Interfejs pozwala klasa przejąć kontrolę nad jego późno związana zachowania.
Kiedy używasz dynamic
słowa kluczowego do interakcji z IDynamicMetaObjectProvider
implementacją, DLR wywołuje IDynamicMetaObjectProvider
metody, a sam obiekt decyduje, co zrobić.
ExpandoObject
I DynamicObject
zajęcia są implementacje IDynamicMetaObjectProvider
.
ExpandoObject
to prosta klasa, która umożliwia dodawanie członków do instancji i używanie ich jako dynamic
sojuszników.
DynamicObject
to bardziej zaawansowana implementacja, którą można odziedziczyć w celu łatwego zapewnienia dostosowanego zachowania.
Postaram się udzielić jaśniejszej odpowiedzi na to pytanie, aby jasno wyjaśnić, jakie są różnice między dynamiką ExpandoObject
a DynamicObject
.
Bardzo szybko dynamic
to słowo kluczowe. To nie jest typ jako taki. Jest to słowo kluczowe, które mówi kompilatorowi, aby ignorował sprawdzanie typu statycznego w czasie projektowania i zamiast tego używał późnego wiązania w czasie wykonywania. Więc nie będziemy spędzać dużo czasu dynamic
w dalszej części tej odpowiedzi.
ExpandoObject
i DynamicObject
rzeczywiście są typami. NA POWIERZCHNI wyglądają bardzo podobnie do siebie. Obie klasy implementują IDynamicMetaObjectProvider
. Jednak kop głębiej, a przekonasz się, że wcale NIE są podobne.
DynamicObject jest częściową implementacją, która IDynamicMetaObjectProvider
ma być wyłącznie punktem wyjścia dla programistów do implementacji własnych niestandardowych typów obsługujących dynamiczne wysyłanie z niestandardowym podstawowym zachowaniem pamięci i pobierania, aby dynamiczna wysyłka działała.
Krótko mówiąc, użyj DynamicObject, gdy chcesz stworzyć własne typy, które mogą być używane z DLR i pracować z dowolnymi NIESTANDARDOWYMI zachowaniami, jakie chcesz.
Przykład: Wyobraź sobie, że chciałbyś mieć typ dynamiczny, który zwraca niestandardową wartość domyślną za każdym razem, gdy podejmowana jest próba pobrania na elemencie, który NIE istnieje (tj. Nie został dodany w czasie wykonywania). Domyślnie powie: „Przepraszam, w tym słoiku nie ma ciasteczek!”. Jeśli chcesz dynamicznego obiektu, który zachowuje się w ten sposób, musisz kontrolować, co się dzieje, gdy pole nie zostanie znalezione. ExpandoObject nie pozwoli ci tego zrobić. Musisz więc utworzyć własny typ z unikalnym dynamicznym zachowaniem rozdzielczości elementu członkowskiego (wysyłką) i użyć go zamiast gotowego ExpandoObject
.
Możesz utworzyć typ w następujący sposób: (Uwaga, poniższy kod jest tylko ilustracją i może nie działać. Aby dowiedzieć się, jak prawidłowo używać DynamicObject, istnieje wiele artykułów i samouczków w innych miejscach).
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;
}
}
Teraz moglibyśmy użyć tej wyimaginowanej klasy, którą właśnie utworzyliśmy jako typ dynamiczny, który ma bardzo niestandardowe zachowanie, jeśli pole nie istnieje.
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
jest PEŁNĄ implementacją IDynamicMetaObjectProvider
, w której zespół .NET Framework podjął wszystkie te decyzje za Ciebie. Jest to przydatne, jeśli nie potrzebujesz żadnego niestandardowego zachowania i uważasz, że ExpandoObject działa dla Ciebie wystarczająco dobrze (w 90% przypadków ExpandoObject
jest wystarczająco dobry). Na przykład zobacz poniższe, a dla ExpandoObject projektanci zdecydowali się zgłosić wyjątek, jeśli dynamiczny element członkowski nie istnieje.
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) { ... }
Podsumowując, ExpandoObject
jest to po prostu jeden z wstępnie wybranych sposobów rozszerzenia DynamicObject o pewne dynamiczne zachowania wysyłania, które prawdopodobnie będą dla Ciebie odpowiednie , ale mogą nie zależeć od Twoich konkretnych potrzeb.
Natomiast DyanmicObject
jest to pomocnik BaseType, który sprawia, że implementowanie własnych typów z unikalnymi dynamicznymi zachowaniami jest proste i łatwe.
Przydatny samouczek, na którym opiera się większość powyższego przykładowego źródła.
DynamicObject
: podczas zastępowania TryGetMember
, jeśli zwrócisz false, RuntimeBinderException
zostanie wyrzucony podczas próby dostępu do nieistniejącej właściwości. Aby fragment faktycznie działał, należy wrócić true
.
Zgodnie ze specyfikacją języka C # dynamic
jest to deklaracja typu. Oznacza dynamic x
to, że zmienna x
ma typ dynamic
.
DynamicObject
jest typem, który ułatwia implementację, IDynamicMetaObjectProvider
a tym samym zastępuje specyficzne zachowanie wiązania dla typu.
ExpandoObject
jest typem, który działa jak worek mienia. Oznacza to, że możesz dodawać właściwości, metody i tak dalej do dynamicznych instancji tego typu w czasie wykonywania.
dynamic
nie jest rzeczywistym typem ... jest to tylko wskazówka, aby powiedzieć kompilatorowi, aby używał późnego wiązania dla tej zmiennej. dynamic
zmienne są faktycznie deklarowane jak object
w MSIL
Powyższy przykład DynamicObject
nie wyjaśnia wyraźnie różnicy, ponieważ w zasadzie implementuje funkcjonalność, która jest już dostarczona przez ExpandoObject
.
W dwóch wymienionych poniżej linkach jest bardzo jasne, że za pomocą DynamicObject
można zachować / zmienić rzeczywisty typ ( XElement
w przykładzie użytym w poniższych linkach) i lepszą kontrolę właściwości i metod.
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;
}
}
}