Dieser Artikel wurde maschinell übersetzt.

C# 4.0

Neue C#-Funktionen in .NET Framework 4

Chris Burrows

Seit der ersten Version 2002 wurde die C#-Programmiersprache verbessert damit Programmierer besser, mehr verwaltbaren Code schreiben. Die Erweiterungen haben das Hinzufügen von Features wie z. B. generische Typen, auf NULL festlegbare Werttypen, Lambda-Ausdrücke, Iterator Methoden, partielle Klassen und eine lange Liste von anderen nützlichen Sprachkonstrukte stammen. Und die Änderungen wurden häufig, erteilen Sie Microsoft .NET Framework-Bibliotheken, die entsprechende Unterstützung begleitet.

Dieser Trend in Richtung erhöhter Benutzerfreundlichkeit weiterhin in c# 4.0. Die Ergänzungen stellen allgemeine Aufgaben im Zusammenhang mit generische Typen, legacy-Interop und Arbeiten mit dynamischen Objektmodelle viel einfacher. Dieser Artikel soll eine High-Level-Umfrage dieser neuen Funktionen bieten. Ich werde mit Abwandlung generischer Typen beginnen, und sehen Sie sich die älteren und dynamische Interop-Features.

Kovarianz und Kontravarianz

Kovarianz und Kontravarianz am besten mit einem Beispiel vorgestellt und das beste ist im Framework. In System.Collections.Generic, IEnumerable <T> und IEnumerator <T> darstellen, bzw. ein Objekt, das eine Folge von T und den Enumerator (oder Iterator) ist, wird die Arbeit von die Reihenfolge durchlaufen. Diese Schnittstellen haben viel Schwerarbeit für lange Zeit vorgenommen werden, bieten Unterstützung für die Implementierung der Foreach-Schleife-Konstrukt. In c# 3.0, Sie wurden sogar noch herausragender wegen Ihrer zentralen Rolle in LINQ und LINQ to Objects – sind die Schnittstellen .NET Sequenzen darstellen.

Also wenn eine Klassenhierarchie mit, z. B. einen Mitarbeiter und ein Manager geben, die davon abgeleitet (Manager sind Mitarbeiter, nachdem alle), dann würde Erwartungen den folgenden Code dazu?

IEnumerable<Manager> ms = GetManagers();
IEnumerable<Employee> es = ms;

Es scheint, als ob eine sein sollte in der Lage, eine Folge von Managern zu behandeln, als handele es sich um eine Folge von Mitarbeitern. In c# 3.0, die Aufgabe schlägt fehl, sondern der Compiler teilt Ihnen keine Konvertierung. Schließlich hat es nicht, was die Semantik von IEnumerable <T> sind. Dies kann eine beliebige Schnittstelle sein so, dass für jede beliebige Schnittstelle IFoo <T>, warum ein IFoo <Manager> mehr oder weniger für ein IFoo <Employee> ersetzbaren wäre?

In c# 4.0 funktioniert jedoch der Arbeitsauftrag, weil IEnumerable <T>, zusammen mit einigen anderen Schnittstellen hat sich geändert, eine Änderung, durch die neue Unterstützung in c# für die Kovarianz der Typparameter aktiviert.

IEnumerable <T> ist besser als die willkürliche IFoo <T> spezielle sein, da, obwohl es auf den ersten Blick nicht offensichtlich ist Member, die den Typparameter T (GetEnumerator in IEnumerable <T>) und die Current-Eigenschaft IEnumerator <T> T tatsächlich nur an der Position eines Rückgabewerts verwenden. So erhalten Sie nur einen Manager außerhalb der Sequenz und nie setzen Sie eine in.

Im Gegensatz dazu der Liste <T> vorstellen. Erstellen einer Liste <Manager> ersetzbaren für eine Liste <Employee> ein Datenverlust aufgrund der folgenden wäre:

List<Manager> ms = GetManagers();
List<Employee> es = ms; // Suppose this were possible
es.Add(new EmployeeWhoIsNotAManager()); // Uh oh

Dies zeigt, wenn Sie vermuten, dass Sie einer Liste <Employee> anzeigen, können Sie jedem Mitarbeiter einfügen. Aber die betreffenden Liste ist tatsächlich eine Liste <Manager>, so, dass Einfügen von nicht-Manager durchgeführt werden muss. Sie haben die Typsicherheit verloren, wenn Sie dies zulassen. Liste <T> kann nicht in t. covariant sein.

Neues Sprachfeature in c# 4.0 ist die Möglichkeit zum Definieren von Typen, z. B. die neue IEnumerable <T>, die Konvertierungen untereinander zugeben, wenn die betreffende Typparameter einige Beziehung zueinander aufweisen. Hierbei handelt es sich um welche der .NET Framework-Entwickler, die IEnumerable <T> verwendet habe, und dies ist Ihr Code aussieht (natürlich vereinfacht):

public interface IEnumerable<out T> { /* ... */ }

Beachten Sie das Out-Schlüsselwort ändern die Definition des Typparameters t. Wenn der Compiler Dies sieht, es T als kovariant Mark und überprüfen, in der Definition der Schnittstelle alle Verwendungen von T Stand snuff (in anderen Worten, die Sie in aus nur Positionen verwendet – deshalb dieses Schlüsselwort ausgewählt wurde).

Warum ist dies die Kovarianz bezeichnet? Nun, ist es am einfachsten, finden Sie unter beim start Pfeile zeichnen. Verwenden Sie konkrete werden let’s die Manager und Mitarbeiter-Typen. Da eine Vererbungsbeziehung zwischen diesen Klassen ist, besteht eine implizite Verweiskonvertierung von Manager Mitarbeiter:

Manager → Mitarbeiter

Und nun aufgrund der Anmerkung von T in IEnumerable < out T >, es gibt auch eine implizite Verweiskonvertierung von IEnumerable <Manager> in IEnumerable <Employee>. Das ist, was für die Anmerkung bereitstellt:

IEnumerable <Manager> → IEnumerable <Employee>

Dadurch wird die Kovarianz, in dieselbe Richtung der Pfeile in jedem der beiden Beispiele zeigen bezeichnet. Wir beginnen mit zwei Typen, Manager und Mitarbeiter. Wir haben diese IEnumerable <Manager> und IEnumerable <Employee> von neue Typen. Die neuen Typen konvertieren, genauso wie die alten.

Dies geschieht, rückwärts ist Kontravarianz. Sie können erwarten, dass dies geschehen kann, wenn der Typparameter T nur als Eingabe verwendet wird und nach rechts werden. Der System-Namespace enthält z. B. eine Schnittstelle namens IComparable <T>, die über eine einzige Methode namens CompareTo verfügt:

public interface IComparable<in T> { 
  bool CompareTo(T other); 
}

Haben Sie einen IComparable <Employee> sollten Sie möglicherweise die dies als handele es sich um einen IComparable <Manager>, behandeln, da das einzige, was, das Sie tun können, Mitarbeiter in der Schnittstelle ist. Da ein Manager ein Angestellter ist, sollte funktionieren durch Einfügen eines Managers in, und dies der Fall ist. Das in-Schlüsselwort ändert T in diesem Fall, und in diesem Szenario funktioniert fehlerfrei:

IComparable<Employee> ec = GetEmployeeComparer();
IComparable<Manager> mc = ec;

Dies ist Kontravarianz bezeichnet, da es sich bei der Pfeil dieses Mal rückgängig gemacht haben:

Manager → Mitarbeiter
IComparable <Manager> ← IComparable <Employee>

Daher ist das Sprach-Feature zum Zusammenfassen von ziemlich einfach: Können Sie das Schlüsselwort in hinzufügen oder aus wenn Sie definieren einen Typparameter, und dieser Vorgang bietet Ihnen also freie zusätzliche Konvertierungen. Es gibt jedoch einige Einschränkungen.

Erstens dies funktioniert mit generischen Schnittstellen und nur delegiert. Einen generischer Typparameter kann nicht auf eine Klasse oder Struktur auf diese Weise deklariert werden. Eine einfache Möglichkeit, dies zu rationalisieren, ist, dass Delegaten sind sehr ähnlich, wie Schnittstellen, die nur eine Methode und in jedem Fall Klassen häufig dieser Abhandlung unzulässiges aufgrund der Felder wäre. Sie können sich jedes Feld der generischen Klasse als eine Eingabe und Ausgabe, je nachdem, ob Sie es geschrieben oder daraus gelesen. Wenn diese Felder Typparameter beinhalten, die Parameter kann weder Covariant- oder Contravariant.

Zweitens immer, wenn Sie eine Schnittstelle oder einen Covariant- oder Contravariant-Typparameter zu delegieren, sind Sie erteilt neue Konvertierungen für diesen Typ nur dann aus, wenn die Typargumente in der Verwendung der Schnittstelle (nicht die Definition), Verweistypen sind. Z. B. da Int ein Werttyp ist, wird nicht die IEnumerator <int> IEnumerator <object>, konvertieren, obwohl anscheinend möchten Sie sollte:

IEnumerator <int> image: right arrow with slash  IEnumerator <object>

Der Grund für dieses Verhalten ist, dass die Konvertierung die Typ Darstellung beibehalten muss. Die Int-Objekt die Konvertierung zulässig sind, würde die Current-Eigenschaft für das Ergebnis aufrufen unmöglich, da Typ Int-Wert eine andere Darstellung auf dem Stapel, hat als Objektverweis. Alle Verweistypen haben dieselbe Darstellung auf dem Stapel jedoch so, dass nur Typargumente, die Verweistypen sind diese zusätzlichen Konvertierungen ergeben.

Die meisten C#-Entwickler verwenden zum Glück mit diesem neuen Sprachfeature der sehr wahrscheinlich – Sie erhalten weitere Konvertierungen von Framework-Typen und weniger Compilerfehler bei der Verwendung einiger Typen von .NET Framework (IEnumerable <T>, IComparable <T>, Func <T>, Aktion <T>, u. a.). Und in der Tat ist jeder Benutzer Entwerfen einer Bibliothek mit generischen Schnittstellen und Delegaten frei, um die neue ein- und Auschecken von Typparametern bei Bedarf zum Leben für Ihre Benutzer vereinfachen verwenden.

Dieses Feature wird durch die Art der Common Language Runtime-Unterstützung benötigt, aber die Unterstützung wurde immer vorhanden. Layout für mehrere Versionen jedoch inaktiv, weil keine Sprache davon verwenden. Vorherige Versionen von c# zulässig auch einige begrenzten Konvertierungen, die kontravariant wurden. Insbesondere können Sie Sie Delegaten von Methoden, die kompatible Rückgabetypen hatte. Darüber hinaus wurden Arraytypen immer covariant. Diese vorhandenen Features unterscheiden sich von der neue in c# 4.0, die tatsächlich können Sie Ihre eigenen Typen zu definieren, die covariant und kontravariant in einigen Ihre Typparameter.

Dynamischer Dispatch

An der Interop-Features in c# 4.0 ist das beginnend mit was vielleicht größte Änderung.

C# unterstützt jetzt die dynamisches spätes Binden. Die Sprache immer stark typisiert wurde und weiterhin in der Version 4.0 sein. Microsoft ist überzeugt, dadurch wird c# einfach zu verwendende, schnelle und für die Arbeit .NET Programmierer sind Übertragung an. Doch manchmal Sie die Kommunikation mit Systemen, die nicht auf .NET basieren müssen.

Bisher gab es mindestens zwei Ansätze. Die erste war einfach fremde Modell direkt in .NET als Proxy zu importieren. COM-Interop stellt ein Beispiel bereit. Seit der ursprünglichen Veröffentlichung von .NET Framework hat er diese Strategie verwendet, mit einem Tool namens TLBIMP, mit dem neue .NET Proxy-Typen, die Sie verwenden können, direkt von c# erstellt.

LINQ-zu-SQL, im Lieferumfang von c# 3.0, enthält ein Tool namens SQLMETAL, der eine vorhandene Datenbank in c# Proxyklassen für die Verwendung mit Abfragen importiert. Außerdem finden Sie ein Tool, das in der C#-Klassen (Windows Management Instrumentation, WMI) importiert. Viele Technologien ermöglichen es Ihnen, c# (oft mit Attributen) zu schreiben, und führen Sie dann Interop-Verwendung handschriftlichen Code als Grundlage für externe Aktionen, wie z. B. LINQ-SQL, Windows Communication Foundation (WCF) und Serialisierung.

Der zweite Ansatz verwirft das C#-Typsystem vollständig – Sie Zeichenfolgen und Daten in Ihrem Code einbetten. Dies ist, werden bei jedem Sie Code, sagen schreiben, ruft eine Methode auf ein JScript-Objekt oder wenn Sie eine SQL-Abfrage in ADO.NET-Anwendung einbetten. Dies sind selbst wenn Sie die Bindung zur Laufzeit mittels Reflektion, obwohl der Interop-in diesem Fall mit .NET selbst verzögern erreicht.

Dynamische-Schlüsselwort in c# ist eine Antwort auf den Umgang mit den optimal verwaltet, andere Ansätze. Mit einem einfachen Beispiel Let’s starten – Reflektion. Normalerweise erfordert es viel Standardcode Infrastruktur, z. B.:

object o = GetObject();
Type t = o.GetType();
object result = t.InvokeMember("MyMethod", 
  BindingFlags.InvokeMethod, null, 
  o, new object[] { });
int i = Convert.ToInt32(result);

Mit dem dynamischen-Schlüsselwort anstelle eines Aufrufs einer Methode MyMethod auf ein Objekt mithilfe von Reflektion auf diese Weise erfahren Sie jetzt den Compiler an, o als dynamische behandeln, und alle Analyse bis zur Laufzeit verzögert. Code, der diese Aufgaben übernimmt sieht folgendermaßen aus:

dynamic o = GetObject();
int i = o.MyMethod();

Es funktioniert, und es führt das gleiche mit Code, der viel weniger unübersichtlich wird.

Der Wert dieses verkürzte, vereinfachtes C#-Syntax ist vielleicht mehrere deaktivieren, wenn Sie die Klasse ScriptObject betrachten, die Vorgänge für ein JScript-Objekt unterstützt. Die Klasse verfügt über eine weitere und andere Parameter außer hat in Silverlight eine Invoke-Methode (Beachten Sie den Unterschied zwischen den Namen) in Wirklichkeit mit weniger Parameter InvokeMember-Methode. Weder dieser sind identisch, Sie zum Aufrufen einer Methode für ein Objekt IronPython oder IronRuby oder auf eine beliebige Anzahl von nicht benötigen würde-C#-Objekte in Kontakt mit geliefert werden kann.

Zusätzlich zu den Objekten, die dynamische Sprachen stammen, finden Sie eine Vielzahl von Datenmodellen, die grundsätzlich dynamisch sind und verschiedene APIs, z. B. HTML-DOM-Objekte, das DOM System.XML und XLinq-Modell für die XML-Unterstützung. COM-Objekte sind häufig dynamisch und profitieren von der Verzögerung, die einige Compiler Analyse ausgeführt.

C# 4.0 bietet im Wesentlichen eine vereinfachte, konsistente Ansicht der dynamischen Vorgänge. Es nutzen, müssen Sie, lediglich angeben, dass ein angegebener Wert ist dynamisch, um sicherzustellen, dass die Analyse aller Vorgänge auf den Wert bis zur Laufzeit verzögert werden wird.

Dynamische ist ein integrierter Typ und eine spezielle Pseudo-keyword gibt es in c# 4.0. Beachten Sie jedoch, dynamische unterscheidet sich von der Variable Variablen, die mit der Var tatsächlich besitzen einen starken Typ, aber der Programmierer hat bis zu der Compiler herausfinden, ihn verlassen. Wenn der Programmierer dynamische verwendet, der Compiler weiß nicht Typ verwendet wird – der Programmierer bleibt bis zur Laufzeit herauszufinden.

Dynamische und die DLR

Die Infrastruktur, die zur Laufzeit diese dynamischen Operationen unterstützt, wird die Dynamic Language Runtime (DLR) bezeichnet. Diese neuen .NET Framework-4-Bibliothek wird auf die CLR wie andere verwaltete Bibliothek ausgeführt. Er ist verantwortlich für die brokering jeder dynamischen Vorgang zwischen der Sprache, die es gestartet und das Objekt, dem es in auftritt. Wenn eine dynamische Operation vom Objekt behandelt wird nicht auf auftreten, behandelt eine Laufzeitkomponente des C#-Compilers die Bindung. Eine vereinfachte und unvollständige Architekturdiagramm sieht etwa wie Abbildung 1 .

image: The DLR Runs on Top of the CLR

Abbildung 1 die DLR ausgeführt wird, im Vordergrund der CLR

Das interessante an einen dynamischen Vorgang, z. B. ein dynamischer Methodenaufruf ist, dass das Empfängerobjekt erhält die Möglichkeit, sich selbst in die Bindung zur Laufzeit eingefügt und kann daher die Semantik für einen bestimmten Vorgang des dynamischen vollständig ermitteln. Betrachten Sie beispielsweise den folgenden Code:

dynamic d = new MyDynamicObject();
d.Bar("Baz", 3, d);

Wenn MyDynamicObject definiert wurde, wie hier gezeigt, können Sie sich vorstellen was geschieht:

class MyDynamicObject : DynamicObject {
  public override bool TryInvokeMember(
    InvokeMemberBinder binder, 
    object[] args, out object result) {

    Console.WriteLine("Method: {0}", binder.Name);
    foreach (var arg in args) {
      Console.WriteLine("Argument: {0}", arg);
    }

    result = args[0];
    return true;
  }
}

In der Tat druckt der Code:

Method: Bar
Argument: Baz
Argument: 3
Argument: MyDynamicObject

Durch die Deklaration von dynamischen Typ d, verwendet der Code, der die Instanz MyDynamicObject effektiv aus der Kompilierung für die Arbeitsgänge d beteiligt wird überprüft. Verwenden von dynamischen bedeutet “ ich Don ’t wissen, welche Art dies geschieht, damit ich Don ’t wissen, welche Methoden oder Eigenschaften jetzt sind. Compiler, lassen Sie diese über und ermitteln Sie wenn tatsächlich ein Objekt zur Laufzeit haben,. ” Damit der Aufruf von Leiste kompiliert, obwohl der Compiler nicht weiß, was es bedeutet. Dann zur Laufzeit auf das Objekt selbst ist aufgefordert Vorgehensweise, mit diesem Aufruf von Outlook-Leiste. Was ist TryInvokeMember weiß, wie behandeln.

Nehmen Sie nun an, dass Sie anstelle einer MyDynamicObject Python-Objekt verwendet:

dynamic d = GetPythonObject();
d.bar("Baz", 3, d);

Ist das Objekt der hier aufgeführten Dateien dann auch der Code funktioniert, und die Ausgabe ist ähnlich:

def bar(*args):
  print "Method:", bar.__name__
  for x in args:
    print "Argument:", x

Im Hintergrund für jede Verwendung von dynamischen Wert generiert der Compiler eine Reihe von Code, der initialisiert und eine DLR-CallSite verwendet. Die CallSite enthält alle Informationen, die zur Laufzeit, darunter z. B. den Namen der Methode binden zusätzliche Daten, z. B., ob die Operation hat in einem geprüften Kontext und Informationen über die Argumente und deren Typen platzieren erforderlich sind.

Dieser Code, wenn Sie beibehalten, wäre jedes Bit als hässlichen der Reflektion Code gezeigte Code ScriptObject oder Zeichenfolgen, die XML-Abfragen enthalten. Das ist der Punkt des dynamischen Features in c# – Sie Don ’t Code schreiben müssen, dass!

Beim dynamische-Schlüsselwort verwenden, kann Ihr Code ziemlich die Ihren Wünschen: wie einen einfacher Methodenaufruf einen Aufruf an einen Indexer, einen Operator, wie z. B. +, eine Umwandlung oder sogar Zusammensetzungen wie += oder ++. Sie können sogar dynamische Werte in Anweisungen – z. B. if(d) und Foreach(var x in d). Eine verkürzte wird ebenfalls unterstützt, mit Code wie beispielsweise d & & ShortCircuited oder d?? ShortCircuited.

Der Wert des mit die DLR bieten eine allgemeine Infrastruktur für diese Arten von Operationen ist, dass Sie nicht mehr auftreten, mit einer anderen API für jedes dynamische Modell verarbeiten, die Code für – es ist nur eine einzelne API. Und müssen Sie nicht selbst verwendet werden. Der C#-Compiler können Sie verwenden, und, sollte Ihnen mehr Zeit für die tatsächlich der Code geschrieben werden soll, die weniger Infrastrukturcode Sie gewährleisten müssen mehr Produktivität für Sie bedeutet.

Die Programmiersprache c# bietet keine Verknüpfungen für dynamische Objekte definieren. Dynamische in c# ist alles über beanspruchen und die Verwendung von dynamische Objekte. Berücksichtigen Sie die folgenden Punkte:

dynamic list = GetDynamicList();
dynamic index1 = GetIndex1();
dynamic index2 = GetIndex2();
string s = list[++index1, index2 + 10].Foo();

In diesem Code wird kompiliert, und viel dynamischen Vorgänge enthält. Zunächst ist die dynamische Pre-increment auf index1, dann die DLL mit index2 hinzufügen. Anschließend wird eine dynamische Indexer Get Liste aufgerufen. Das Produkt dieser Operationen Ruft den Foo-Member. Schließlich wird das gesamte Ergebnis des Ausdrucks in eine Zeichenfolge konvertiert und in s gespeichert. Fünf dynamische Operationen in einer einzigen Zeile, jeweils zur Laufzeit verteilt.

Der Typ zur Kompilierungszeit jeder dynamischen Vorgang ist dynamisch, selbst, sodass die “ Dynamicness ” Art von Berechnung zur Berechnung fließt. Selbst wenn Sie dynamische Ausdrücke mehrfach vorhanden hadn't, noch werden eine Reihe von dynamischen Operationen. Es gibt noch fünf in dieser Zeile ein:

string s = nonDynamicList[++index1, index2 + 10].Foo();

Da die Ergebnisse der beiden Ausdrücke Indizierung dynamisch sind, ist der Index selbst ebenfalls. Und da das Ergebnis des Indexes dynamisch ist, also ist der Aufruf von Foo. Dann sind Sie konfrontiert, mit einen dynamischen Wert in eine Zeichenfolge konvertieren. In diesem Fall dynamisch, natürlich, da das Objekt eine dynamische werden konnte, die einige spezielle Berechnung angesichts der Anforderung Konvertierung durchführen möchte.

Beachten Sie in den vorherigen Beispielen, dass c# implizite Konvertierungen aus jeder dynamischen Ausdruck in einen beliebigen Typ zulässt. Die Konvertierung in eine Zeichenfolge am Ende ist implizit und eine explizite Typumwandlung Operation nicht erforderlich. In ähnlicher Weise kann einen beliebigen Typ dynamischen implizit konvertiert werden.

In dieser Hinsicht dynamisch ist viel wie-Objekt und die Ähnlichkeiten Don ’t nicht mehr vorhanden. Wenn der Compiler die Assembly und muss eine dynamische Variable ausgeben gibt, wird er mithilfe den Typ Object und dann speziell zu kennzeichnen. In einigen Sinne dynamische Art der Alias für das Objekt ist, aber er fügt das zusätzliche Verhalten Operationen dynamisch aufgelöst werden, wenn Sie es verwenden.

Können Sie dies beim Konvertieren zwischen generischen Typen, die nur in dynamischen unterscheiden und Objekt; solche Konvertierungen funktioniert immer, da zur Laufzeit eine Instanz der Liste <dynamic> tatsächlich eine Instanz der Liste <object> ist:

List<dynamic> ld = new List<object>();

Außerdem können Sie finden Sie in die Ähnlichkeit zwischen dynamischen und Objekt, wenn Sie versuchen, eine Methode zu überschreiben, die mit einem Objektparameter deklariert wird:

class C {
  public override bool Equals(dynamic obj) { 
    /* ... */ 
  }
}

Obwohl diese ergänzten-Objekt in Ihrer Assembly auflöst, führen Sie ich gerne als ein realer Typ, dynamische vorstellen da er dient als Erinnerung, die Sie mit den meisten Dinge tun können, die von einem anderen Typ. Sie können es als Typargument oder sagen, als Rückgabewert verwenden. Beispielsweise wird die Definition der Funktion Sie das Ergebnis des Funktionsaufrufs dynamisch zu verwenden, ohne dass deren Rückgabewert in eine dynamische Variable einfügen können:

public dynamic GetDynamicThing() { 
  /* ... */ }

Es gibt viel mehr Details über die Möglichkeit, dynamische behandelt und verteilt wird, aber Sie Don ’t müssen wissen, das Feature zu verwenden. Der grundlegende Gedanke ist, dass Code, der aussieht wie c# geschrieben werden kann und wenn ein Teil der von Ihnen geschriebenen Code dynamisch ist, der Compiler Sie allein bis zur Laufzeit verlassen.

Eine letzte Thema bezüglich der dynamischen abdecken soll: Fehler. Da der Compiler kann nicht überprüfen möchten, ob das dynamische, was Sie wirklich die Foo aufgerufene Methode verfügt, kann nicht es Sie ein Fehler verursacht. Natürlich, bedeutet nicht, dass der Aufruf von Foo zur Laufzeit funktioniert. Es funktioniert, aber es gibt eine Menge von Objekten, die Don ’t haben eine Methode namens „ Foo. Wenn Ihr Ausdruck zur Laufzeit binden, macht der Binder die beste versucht, Ihnen eine Ausnahme, die ist mehr oder weniger genau das, was der Compiler Sie mitgeteilt haben würden Sie hadn't verwendet zunächst dynamische.

Betrachten Sie folgenden Code:

try 
{
  dynamic d = "this is a string";
  d.Foo();
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
{
  Console.WriteLine(e.Message);
}

Ich habe hier eine Zeichenfolge und Zeichenfolgen müssen natürlich keine Methode namens „ Foo. Wenn die Zeile, die Foo ruft ausgeführt wird, wird die Bindung schlägt fehl, und erhalten Sie ein RuntimeBinderException. Dies ist, was das vorherige Programm druckt:

'string' does not contain a definition for 'Foo'

Also genau die Fehlermeldung angezeigt, die Sie als C#-Programmierer, erwarten.

Benannte Argumente und optionale Parameter

Zusätzlich ein anderes in c# unterstützen Methoden jetzt optionale Parameter mit Standardwerten, sodass beim Aufrufen einer solchen Methode können Sie diesen Parameter auslassen. Sie können dies in Aktion in dieser Klasse Car anzeigen:

class Car {
  public void Accelerate(
    double speed, int? gear = null, 
    bool inReverse = false) { 

    /* ... */ 
  }
}

Auf diese Weise können Sie die Methode aufrufen:

Car myCar = new Car();
myCar.Accelerate(55);

Dies hat dieselbe Wirkung wie das:

myCar.Accelerate(55, null, false);

Es ist identisch, da der Compiler die Standardwerte eingefügt werden, die Sie nicht angeben.

C# 4.0 auch können Sie Methoden aufrufen, indem Sie einige Argumente durch Namen angeben. Auf diese Weise können Sie ein Argument mit einem optionalen Parameter übergeben, ohne auch übergeben von Argumenten für alle Parameter, die davor stehen.

Angenommen, Sie rufen Sie die Beschleunigung in umgekehrter Reihenfolge zu wechseln möchten, aber der Ausrüstung-Parameter angeben möchten. Sie können nun, dies tun:

myCar.Accelerate(55, inReverse: true);

Dies ist eine neue C#-4.0-Syntax, und es ist identisch, als ob Sie geschrieben hatte:

myCar.Accelerate(55, null, true);

In der Tat unabhängig davon, ob Sie in der Methode, die Sie aufrufen Parameter optional sind, können Sie Namen verwenden, um Argumente zu übergeben. Diese beiden Aufrufe sind z. B. die zulässige und mit einer anderen identisch:

Console.WriteLine(format: "{0:f}", arg0: 6.02214179e23);
Console.WriteLine(arg0: 6.02214179e23, format: "{0:f}");

Wenn Sie eine Methode, die eine lange Liste von Parametern aufrufen, können noch Sie Namen als eine Art-Code Dokumentation zu erinnern, welche Parameter ist.

Auf der Oberfläche nicht optionalen Argumente und benannte Parameter wie Interop-Features angezeigt. Sie können ohne jemals selbst denken Interop verwendet werden. Die Motivation für diese Features stammt jedoch von der Office-APIs. Ziehen Sie in Betracht, z. B. Word-Programmierung und etwas so einfaches wie die SaveAs-Methode des Document-Schnittstelle. Diese Methode hat 16 Parameter, die optional sind. Mit früheren Versionen von c# Wenn Sie diese Methode aufrufen, müssen Sie Code schreiben, der wie folgt aussieht:

Document d = new Document();
object filename = "Foo.docx";
object missing = Type.Missing;
d.SaveAs(ref filename, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);

Nun können Sie Folgendes schreiben:

Document d = new Document();
d.SaveAs(FileName: "Foo.docx");

Ich würde sagen, ist eine Verbesserung für alle Benutzer, die mit APIs wie folgt arbeitet. Und der Programmierer, die Office-Programme schreiben, um müssen eine Erleichterung wurde definitiv motivierende Faktor für das Hinzufügen von benannte Argumente und optionalen Parametern für die Sprache.

Nun, wenn eine Bibliothek für .NET schreiben und Hinzufügen von Methoden, die optionale Parameter in Erwägung ziehen, Sie sind Angesichts einer Auswahl. Sie können optionale Parameter hinzufügen, oder können Sie für die Jahre haben C#-Programmierer: eine Einführung in Überladungen. In dem Beispiel Car.Accelerate kann die letzte Entscheidung führen Sie einen Typ erstellen, der wie folgt aussieht:

class Car {
  public void Accelerate(uint speed) { 
    Accelerate(speed, null, false); 
  }
  public void Accelerate(uint speed, int? gear) { 
    Accelerate(speed, gear, false); 
  }
  public void Accelerate(uint speed, int? gear, 
    bool inReverse) { 
    /* ... */ 
  }
}

Wählen das Modell, das die Bibliothek entspricht, die Sie schreiben, bleibt Ihnen überlassen. Da c# optionale Parameter bis jetzt hat noch nicht, ist das .NET Framework (einschließlich der .NET Framework-4) Überladungen verwenden. Wenn Sie beschließen, mischen und Kombinieren von Überladungen mit optionalen Parametern, hat die Überladungsauflösung c# deaktivieren Gleichstand unterbrechende Regeln, um zu bestimmen, welche Überladung aufgerufen wird unter bestimmten Umständen.

Indizierte Eigenschaften

Nur beim Schreiben von Code für eine COM-Interop-API, werden einige kleinere Sprachfeatures in c# 4.0 unterstützt. Die Word-Interop in der vorherigen Abbildung ist ein Beispiel.

C#-Code wurde immer das Konzept eines Indexers, dass Sie eine Klasse, effektiv auf Instanzen dieser Klasse den []-Operator überladen hinzufügen können. In diesem Sinne des Indexers wird auch einen Standardindexer bezeichnet, da das Programm ist keinen Namen erhält und aufrufenden keinen Namen erfordert. Einige COM-APIs haben auch Indexer, die standardmäßig nicht zu sagen, dass Sie diese effektiv einfach mithilfe von [] Aufrufen nicht möglich ist, müssen Sie einen Namen angeben. Sie können alternativ eine indizierte Eigenschaft als Eigenschaft vorstellen, die einige zusätzliche Argumente annimmt.

C# 4.0 unterstützt indizierte Eigenschaften auf COM-Interop-Typen. In c#, die indizierten Eigenschaften haben keine Typen definieren, aber Sie können Sie verwenden, sofern Sie usw. ein COM-Typ tun. Ein Beispiel für C#-Code, der dies aussieht, sollten Sie die Range-Eigenschaft in einem Excel-Arbeitsblatt:

using Microsoft.Office.Interop.Excel;

class Program {
  static void Main(string[] args) {
    Application excel = new Application();
    excel.Visible = true;

    Worksheet ws = 
      excel.Workbooks.Add().Worksheets["Sheet1"];
    // Range is an indexed property
    ws.Range["A1", "C3"].Value = 123; 
    System.Console.ReadLine();
    excel.Quit();
  }
}

In diesem Beispiel nicht Range [“ A1 ”, “ C3 ”] eine Eigenschaft namens „ Bereich, der ein Element zurückgibt, die indiziert werden können. Es ist ein Aufruf an einen Range-Accessor mit A1 und C3 übergibt. Und auch wenn Value nicht indizierte Eigenschaft aussehen könnte, ebenfalls stellt eine! Alle Argumente sind optional, und da es sich um eine indizierte Eigenschaft handelt, lassen Sie diese durch Angabe werden nicht in allen. Bevor die unterstützte Sprache Eigenschaften indizierten, würden Sie den Anruf wie folgt geschrieben haben:

ws.get_Range("A1", "C3").Value2 = 123;

Hier ' Value2 ' ist eine Eigenschaft, die einfach hinzugefügt wurde daran, dass die indizierte Eigenschaft Value vor zu c# 4.0 verwendet würde nicht.

Das Ref-Schlüsselwort an COM-Aufruf Standorten auslassen

Einige COM-APIs wurden mit vielen Parametern, die als Verweis übergeben werden, selbst wenn Ihnen die Implementierung Zurückschreiben nicht geschrieben. In der Office-Suite Word hervorsticht als Beispiel, dessen COM-APIs, die alle dazu.

Wenn Sie mit einer solchen Library konfrontiert sind, und Argumente als Verweis übergeben werden müssen, können Sie nicht mehr jeder Ausdruck übergeben, ist keine lokale Variable oder ein Feld, und das ist ein großer Kopfschmerzen. In dem Beispiel Word speichern unter können Sie dies in der Praxis, mussten Sie zum Deklarieren einer lokales namens Filename und einem lokalen Namens fehlt nur um die SaveAs-Methode aufrufen, da diese Parameter erforderlich, die als Verweis übergeben werden.

Document d = new Document();
object filename = "Foo.docx";
object missing = Type.Missing;
d.SaveAs(ref filename, ref missing, // ...

Sie haben bemerkt in der neuen C#-Code, der ausgeführt wird, ich einen lokalen für Dateinamen nicht mehr deklariert:

d.SaveAs(FileName: "Foo.docx");

Dies ist möglich, da die neue Ref-Funktion für COM-Interop weglassen. Nun können beim Aufrufen einer COM-Interop-Methode ein Argument Wert anstelle des als Verweis übergeben werden. Wenn Sie dies tun, wird der Compiler eine temporäre lokale in Ihrem Auftrag erstellen und, lokale als Verweis übergeben Sie, falls erforderlich. Natürlich nicht möglich, die Auswirkungen der Aufruf der Methode angezeigt werden soll, wenn die Methode das Argument mutates – Wenn soll, übergeben Sie das Argument durch Ref.

Dies sollte Code erstellen, die APIs wie diese wesentlich Reinigungseinheit verwendet.

Einbetten von COM-Interop-Typen

Dies ist der eines c#-Compilerfeatures als C#-Sprachfeature, aber jetzt können Sie eine COM-Interop-Assembly ohne die Assembly zur Laufzeit zu sein. Ziel ist es, die Belastung der Bereitstellung von COM-interop Assemblys mit der Anwendung verringern.

Wenn COM-Interop in der ursprünglichen Version von .NET Framework eingeführt wurde, wurde das Konzept der primäre Interop-Assembly (PIA) erstellt. Dies war ein Versuch zur Lösung des Problems der Freigabe von COM-Objekten zwischen Komponenten. Wenn verschiedene Interop-Assemblys, die Excel-Tabelle definiert wurde, wäre wir nicht diese Arbeitsblätter zwischen Komponenten, gemeinsam nutzen, da Sie verschiedene Typen von .NET werden würde. Die PIA dies durch nur einmal vorhandener behoben, allen Clients verwendet, und die Typen von .NET immer übereinstimmen.

Jedoch kann eine feine Idee auf Papier, in praktischen bereitstellen eine PIA erweist sich ein Kopfschmerzen, da nur einem und mehreren Anwendungen versuchen, installieren oder deinstallieren es. Themen sind kompliziert, da primären Interop-Assemblys oftmals groß sind, Office nicht mit der standardmäßigen Office-Installationen bereitstellen und Benutzer dieses einzelne Assembly System problemlos umgehen können, indem Sie einfach mit TLBIMP eine eigene Interop-Assembly erstellen.

Im bemühen, diese Situation zu beheben, hätte dies nun zwei Dinge passieren:

  • Die Laufzeit hat die Intelligenz, zwei strukturell identische COM-Interop-Typen zu behandeln, die dieselben eindeutige Merkmale (name, GUID usw.) gemeinsam, nutzen als wären Sie tatsächlich vom gleichen Typ .NET erteilt wurde.
  • Der C#-Compiler nutzt dies durch die Interop-Typen in eine eigene Assembly einfach reproduzieren, beim Kompilieren für die Interop-Assembly zur Laufzeit vorhanden sein muss.

Habe ich einige Details im Interesse des Speicherplatzes weglassen, aber auch ohne Kenntnisse über die Details, ist dies ein weiteres Feature, wie dynamische – sollte problemlos verwenden können. Sie weisen den Compiler Interop-Typen für Sie in Visual Studio integrieren, indem Sie Interop-Typen Embed-Eigenschaft für den Verweis auf True festlegen.

Da das C#-Team hier, um die bevorzugte Methode zum Verweisen auf COM-Assemblys werden erwartet, wird Visual Studio standardmäßig für alle neuen Interop-Verweis ein C#-Projekt hinzugefügt, diese Eigenschaft auf True festgelegt. Wenn Sie den Befehlszeilencompiler (csc.exe) verwenden, um Ihren Code zu erstellen wechseln dann zum Einbetten von Interop Sie die Interop-Assembly, die betreffende mithilfe der/l verweisen müssen Typen anstelle von/r an.

Der Features, die ich in diesem Artikel behandelt haben könnte selbst generieren wesentlich mehr Diskussion und alle Themen verdienen, eigene Beiträge. Ich habe nicht angegeben oder habe über viele Details, aber ich hoffe, dies dient als einen guten Ausgangspunkt für c# 4.0 Erforschen und finden Sie Zeit zu untersuchen, und nehmen Sie diese Features verwenden. Und wenn Sie dies tun, genießen Sie die Vorteile in die Produktivität und die Lesbarkeit der Anwendung Sie entwickelt wurden, um Ihnen hoffentlich.

Chris Burrows ist ein Entwickler bei Microsoft im C#-Compiler Team. Er implementiert, in der C#-Compiler dynamisch und wurde bei der Entwicklung von Visual Studio für neun Jahre. .

Dank an den folgenden technischen Experten für die Überprüfung der in diesem Artikel: Eric Lippert