Wofür wird der dynamische Typ in C # 4.0 verwendet?

C # 4.0 führte einen neuen Typ namens ‘dynamisch’ ein. Es klingt alles gut, aber wofür würde ein Programmierer es verwenden?

Gibt es eine Situation, in der es den Tag retten kann?

    Das Schlüsselwort dynamic ist neu in C # 4.0 und wird verwendet, um dem Compiler mitzuteilen, dass sich der Typ einer Variablen ändern kann oder dass sie erst zur Laufzeit bekannt ist. Betrachten Sie es als in der Lage zu sein, mit einem Objekt zu interagieren, ohne es zu casting.

    dynamic cust = GetCustomer(); cust.FirstName = "foo"; // works as expected cust.Process(); // works as expected cust.MissingMethod(); // No method found! 

    Beachten Sie, dass wir cust nicht als Typ Customer deklarieren oder deklarieren mussten. Da wir es als dynamisch deklariert haben, übernimmt die Laufzeit die function und sucht dann die FirstName-Eigenschaft für uns. Wenn Sie nun eine dynamische Variable verwenden, geben Sie natürlich die Überprüfung des Compiler-Typs auf. Dies bedeutet, dass der Aufruf cust.MissingMethod () kompiliert wird und erst zur Laufzeit fehlschlägt. Das Ergebnis dieser Operation ist eine RuntimeBinderException, da MissingMethod in der Customer-class nicht definiert ist.

    Das obige Beispiel zeigt, wie dynamisch das Aufrufen von Methoden und Eigenschaften funktioniert. Ein weiteres (und potenziell gefährliches) Feature ist die Möglichkeit, Variablen für verschiedene Datentypen wiederzuverwenden. Ich bin mir sicher, dass die Python-, Ruby- und Perl-Programmierer da draußen eine Million Möglichkeiten sehen können, um davon zu profitieren, aber ich benutze C # so lange, dass es sich einfach “falsch” anfühlt.

     dynamic foo = 123; foo = "bar"; 

    OK, also werden Sie sehr wahrscheinlich keinen Code wie oben beschrieben schreiben. Es kann jedoch Zeiten geben, in denen die Wiederverwendung von Variablen nützlich sein kann oder ein schmutziges Stück Altcode aufräumen kann. Ein einfacher Fall, auf den ich oft stoße, muss ständig zwischen Dezimal und Doppeltem wechseln.

     decimal foo = GetDecimalValue(); foo = foo / 2.5; // Does not compile foo = Math.Sqrt(foo); // Does not compile string bar = foo.ToString("c"); 

    Die zweite Zeile kompiliert nicht, da 2.5 als Double eingegeben wird und Zeile 3 nicht kompiliert, da Math.Sqrt ein Double erwartet. Offensichtlich müssen Sie lediglich Ihren Variablentyp umwandeln und / oder ändern, aber es kann Situationen geben, in denen dynamischer Einsatz sinnvoll ist.

     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"); 

    Lesen Sie mehr Feature: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

    Das dynamic Schlüsselwort wurde zusammen mit vielen anderen neuen functionen von C # 4.0 hinzugefügt, um das Sprechen mit Code zu vereinfachen, der in anderen Laufzeiten lebt oder aus anderen Laufzeiten stammt und unterschiedliche APIs verwendet.

    Nimm ein Beispiel.

    Wenn Sie ein COM-Objekt wie das Word.Application Objekt haben und ein Dokument öffnen möchten, enthält die dafür erforderliche Methode nicht weniger als 15 Parameter, von denen die meisten optional sind.

    Um diese Methode aufzurufen, benötigen Sie so etwas (ich vereinfache dies nicht, das ist kein tatsächlicher Code):

     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); 

    Notieren Sie all diese Argumente? Diese müssen seit C # übergeben werden, bevor Version 4.0 keine optionalen Argumente enthielt. In C # 4.0 wurde die Arbeit mit COM-APIs erleichtert, indem Folgendes eingeführt wurde:

    1. Optionale Argumente
    2. ref optional für COM APIs machen
    3. Benannte Argumente

    Die neue Syntax für den obigen Aufruf wäre:

     wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true); 

    Sehen Sie, wie viel einfacher es aussieht, wie viel lesbarer es wird?

    Lasst uns das auseinander brechen:

      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 

    Die Magie ist, dass der C # -Compiler jetzt den notwendigen Code einschleust und mit neuen classn in der Laufzeit arbeitet, um fast genau dasselbe zu tun, was Sie vorher getan haben, aber die Syntax wurde Ihnen vorenthalten, jetzt können Sie sich auf die was und nicht so sehr auf das Wie . Anders Hejlsberg sagt gern, dass man verschiedene “Beschwörungen” anführen muss, was eine Art Wortspiel auf die Magie der ganzen Sache ist, wo man typischerweise seine Hand (n) winken und einige magische Wörter in der richtigen Reihenfolge sagen muss um eine bestimmte Art von Zauberspruch zu erhalten. Die alte API-Art, mit COM-Objekten zu sprechen, war eine Menge davon. Sie mussten durch viele Ringe springen, um den Compiler dazu zu bringen, den Code für Sie zu kompilieren.

    Wenn Sie versuchen, mit einem COM-Objekt zu sprechen, für das Sie keine Schnittstelle oder class haben, wird in C # vor Version 4.0 noch mehr aufgetriggers. Alles, was Sie haben, ist eine IDispatch Referenz.

    Wenn Sie nicht wissen, was es ist, ist IDispatch im Grunde eine Reflektion für COM-Objekte. Mit einer IDispatch Schnittstelle können Sie das Objekt “Was ist die ID-Nummer für die Methode Save” abfragen und Arrays eines bestimmten Typs mit den Argumentwerten erstellen und schließlich eine Invoke Methode für die IDispatch Schnittstelle aufrufen, um die Methode aufzurufen und alle Informationen, die du gesammelt hast, weiterzugeben.

    Die obige Save-Methode könnte so aussehen (das ist definitiv nicht der richtige Code):

     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, ...); 

    All dies für das Öffnen eines Dokuments.

    VB hatte optionale Argumente und Unterstützung für die meisten dieser out-of-the-Box vor langer Zeit, so dass dieser C # -Code:

     wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true); 

    ist im Grunde genommen nur C #, um VB in Bezug auf die Ausdruckskraft einzuholen, aber es richtig zu machen, indem es erweiterbar gemacht wird, und nicht nur für COM. Natürlich ist dies auch für VB.NET oder eine andere Sprache verfügbar, die auf der .NET-Laufzeitumgebung basiert.

    Sie können weitere Informationen über die IDispatch Schnittstelle auf Wikipedia finden: IDispatch, wenn Sie mehr darüber lesen möchten. Es ist wirklich blödes Zeug.

    Was aber, wenn Sie mit einem Python-Objekt sprechen wollten? Es gibt eine andere API als die, die für COM-Objekte verwendet wird, und da Python-Objekte ebenfalls dynamisch sind, müssen Sie auf Reflektionsmagie zurückgreifen, um die richtigen Methoden zum Aufruf zu finden, ihre Parameter usw., aber nicht .NET Reflektion, etwas, das für Python geschrieben wurde, ziemlich genau wie der obige IDispatch-Code, nur ganz anders.

    Und für Ruby? Eine andere API noch.

    JavaScript? Gleiches Angebot, auch andere API.

    Das dynamische Keyword besteht aus zwei Dingen:

    1. Das neue Keyword in C #, dynamic
    2. Eine Gruppe von Laufzeitklassen, die mit den verschiedenen Objekttypen umzugehen weiß, die eine spezifische API implementieren, die das dynamic Schlüsselwort erfordert, und die Aufrufe den richtigen Vorgehensweisen zuordnet. Die API ist sogar dokumentiert. Wenn Sie also Objekte haben, die von einer Laufzeit stammen, die nicht abgedeckt ist, können Sie sie hinzufügen.

    Das dynamic Schlüsselwort ist jedoch nicht dazu gedacht, vorhandenen .NET-Code zu ersetzen. Sicher, Sie können es tun, aber es wurde nicht hinzugefügt, und die Autoren der C # -Programmiersprache mit Anders Hejlsberg an der Spitze, war sehr hartnäckig, dass sie C # immer noch als stark typisierte Sprache betrachten und nicht opfern dieser Grundsatz.

    Dies bedeutet, dass Sie zwar folgenden Code schreiben können:

     dynamic x = 10; dynamic y = 3.14; dynamic z = "test"; dynamic k = true; dynamic l = x + y * z - k; 

    und es kompilieren lassen, es war nicht als eine Art von magischen-lets-out-out-was-du-für-Laufzeit-System gedacht.

    Der ganze Zweck war, es einfacher zu machen, mit anderen Arten von Objekten zu sprechen.

    Im Internet gibt es viel Material über das Keyword, Befürworter, Gegner, Diskussionen, Lobeshymnen, Lob, etc.

    Ich schlage vor, Sie beginnen mit den folgenden Links und googeln dann nach mehr:

    • DevDays 2010: Anders Hejlsberg – C # 4.0 und darüber hinaus
    • Kanal 9: Mads Torgersen – Inside C # 4.0: dynamisches Tippen + +
    • DevX: COM Interop wird viel besser in C # 4.0
    • Scott Hanselman – C # 4 und das dynamische Keyword – Whirlwind Tour um .NET 4 (und Visual Studio 2010) Beta 1

    Ich bin überrascht, dass niemand Mehrfachversand erwähnt hat . Der übliche Weg, um dies zu umgehen, ist über Besuchermuster (Doppel-Versand) und das ist nicht immer möglich, so dass Sie mit gestapelten Schecks enden.

    Hier ist ein Beispiel aus dem wirklichen Leben für eine eigene Anwendung. Anstatt zu tun:

     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()); } 

    Sie verwenden dynamic , um die Factory-function CreateDtoImpl mit dem Laufzeittyp (dynamisch) des Objekts CreateDtoImpl :

     public static MapDtoBase CreateDto(ChartItem item) { return CreateDtoImpl(item as dynamic); // magic here } 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); } 

    Beachten Sie, dass ElevationPoint im ersten Fall eine Unterklasse von MapPoint und wenn sie nicht vor MapPoint platziert MapPoint , wird sie nie erreicht. Dies ist bei dynamic nicht der Fall, da die am besten passende Methode aufgerufen wird.

    Wie Sie aus dem Code ersehen können, war diese function hilfreich, als ich eine Übersetzung von ChartItem-Objekten in ihre serialisierbaren Versionen durchführte. Ich wollte meinen Code nicht mit Besuchern verschmutzen und ich wollte auch meine ChartItem Objekte nicht mit nutzlosen serialisierungsspezifischen Attributen verschmutzen.

    Es erleichtert die Interaktion von statischen typisierten Sprachen (CLR) mit dynamischen (Python, Ruby …), die auf dem DLR (Dynamic Language Runtime) ausgeführt werden, siehe MSDN :

    Beispielsweise könnten Sie den folgenden Code verwenden, um einen Zähler in XML in C # zu erhöhen.

     Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1); 

    Wenn Sie den DLR verwenden, können Sie stattdessen den folgenden Code für dieselbe Operation verwenden.

     scriptobj.Count += 1; 

    MSDN listet diese Vorteile auf:

    • Vereinfacht das Portieren dynamischer Sprachen in .NET Framework
    • Aktiviert dynamische Features in statisch typisierten Sprachen
    • Bietet zukünftige Vorteile des DLR und .NET Framework
    • Ermöglicht das Teilen von Bibliotheken und Objekten
    • Bietet schnellen dynamischen Versand und Aufruf

    Weitere Informationen finden Sie in MSDN .

    Ein Anwendungsbeispiel:

    Sie verbrauchen viele classn mit einer gemeinsamen Eigenschaft ‘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; } } 

    Wenn Sie eine commun-Methode schreiben, die den Wert der CreationDate-Eigenschaft abruft, müssen Sie reflection verwenden:

      static DateTime RetrieveValueOfCreationDate(Object item) { return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item); } 

    Mit dem “dynamischen” Konzept ist Ihr Code viel eleganter:

      static DateTime RetrieveValueOfCreationDate(dynamic item) { return item.CreationDate; } 

    COM-Interop. Vor allem IUnknown. Es wurde speziell dafür entworfen.

    Es wird hauptsächlich von RAD- und Python-Opfern verwendet, um die Codequalität, IntelliSense und die Kompilierzeit-Bug-Erkennung zu zerstören.

    Der beste Anwendungsfall für ‘dynamische’ Variablen war, als ich kürzlich eine Datenzugriffsschicht in ADO.NET geschrieben habe ( mit SQLDataReader ) und der Code die bereits geschriebenen gespeicherten Stored Procedures aufruft. Es gibt Hunderte von gespeicherten gespeicherten Prozeduren, die einen Großteil der Geschäftslogik enthalten. Meine Datenzugriffsschicht musste einige strukturierte Daten in die C # -basierte Geschäftslogikebene zurückgeben, um einige Manipulationen durchzuführen ( obwohl es fast keine gibt ). Jede gespeicherte Prozedur gibt unterschiedliche Daten ( Tabellenspalten ) zurück. Anstatt Dutzende von classn oder Strukturen zu erstellen, um die zurückgegebenen Daten zu speichern und an die BLL zu übergeben, habe ich den folgenden Code geschrieben, der ziemlich elegant und ordentlich aussieht.

     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; } 
    1. Mit pythonnet können Sie dynamische Sprachen wie CPython aufrufen:

    dynamic np = Py.Import("numpy")

    1. Sie können Generics auf dynamic Variablen anwenden, wenn Sie numerische Operatoren auf sie anwenden. Dies bietet Typsicherheit und vermeidet Limitierungen von Generika. Dies ist im Wesentlichen * Ente tippen:

    T y = x * (dynamic)x , wobei der typeof(x) is T

    Es wird zur Laufzeit ausgewertet, sodass Sie den Typ wie in JavaScript beliebig ändern können. Das ist echt:

     dynamic i = 12; i = "text"; 

    Und so können Sie den Typ nach Bedarf ändern. Verwenden Sie es als letztes Mittel; Es ist vorteilhaft, aber ich habe gehört, dass unter den Kulissen eine Menge an generiertem IL passiert und das kann zu einem performancespreis kommen.