web-dev-qa-db-fra.com

A quoi sert le type "dynamique" en C # 4.0?

C # 4.0 a introduit un nouveau type appelé 'dynamique'. Tout cela semble bien, mais à quoi un programmeur l'utiliserait-il?

Y at-il une situation où il peut sauver la journée?

208
Fahad

Le mot-clé dynamic est une nouveauté de C # 4.0 et permet d'indiquer au compilateur que le type d'une variable peut changer ou qu'il n'est pas connu avant l'exécution. Pensez-y comme étant capable d'interagir avec un objet sans avoir à le lancer.

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

Notez que nous n’avons pas besoin de lancer ni de déclarer cust en tant que type Client. Comme nous l'avons déclaré dynamique, le moteur d'exécution prend le relais, puis recherche et définit la propriété FirstName pour nous. Maintenant, bien sûr, lorsque vous utilisez une variable dynamique, vous abandonnez la vérification du type du compilateur. Cela signifie que l'appel cust.MissingMethod () sera compilé et n'échouera pas avant l'exécution. Le résultat de cette opération est une exception RuntimeBinderException car MissingMethod n'est pas défini sur la classe Customer.

L'exemple ci-dessus montre comment la dynamique fonctionne lors de l'appel de méthodes et de propriétés. Une autre fonctionnalité puissante (et potentiellement dangereuse) est la possibilité de réutiliser des variables pour différents types de données. Je suis sûr que les programmeurs Python, Ruby et Perl peuvent penser à un million de façons de tirer parti de cette situation, mais je me sers de C # depuis si longtemps que cela me semble "faux".

dynamic foo = 123;
foo = "bar";

OK, vous n'écrirez donc probablement pas le code comme ci-dessus très souvent. Cependant, il peut arriver que la réutilisation de variables puisse être utile ou nettoyer un morceau de code hérité. Un cas simple que je rencontre souvent est de devoir constamment choisir entre décimales et doubles.

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

La deuxième ligne ne compile pas car 2.5 est saisi en tant que double et la ligne 3 ne compile pas car Math.Sqrt attend un double. Évidemment, tout ce que vous avez à faire est de convertir et/ou de changer le type de votre variable, mais il peut y avoir des situations où la dynamique est logique.

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

Lire la suite: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

169
Pranay Rana

Le mot clé dynamic a été ajouté, ainsi que de nombreuses autres nouvelles fonctionnalités de C # 4.0, afin de simplifier la communication avec du code hébergé ou issu d'autres exécutions, doté de différentes API.

Prenons un exemple.

Si vous avez un objet COM, tel que l'objet Word.Application, et souhaitez ouvrir un document, la méthode à utiliser ne comporte pas moins de 15 paramètres, dont la plupart sont facultatifs.

Pour appeler cette méthode, vous auriez besoin de quelque chose comme ceci (je simplifie, ce n'est pas du code réel):

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

Notez tous ces arguments? Vous devez les transmettre car C # avant la version 4.0 n'avait pas la notion d'arguments optionnels. En C # 4.0, il est plus facile de travailler avec les API COM en introduisant:

  1. Arguments optionnels
  2. Rendre ref facultatif pour les API COM
  3. Arguments nommés

La nouvelle syntaxe pour l'appel ci-dessus serait:

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

Vous voyez à quel point cela semble plus facile, à quel point cela devient plus lisible?

Brisons cela à part:

                                    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

La magie est que le compilateur C # va maintenant injecter le code nécessaire et travailler avec de nouvelles classes dans le runtime, pour faire presque exactement la même chose que précédemment, mais la syntaxe a été cachée, vous pouvez maintenant vous concentrer sur la quoi , et pas tellement sur le comment . Anders Hejlsberg aime dire qu'il faut invoquer différentes "incantations", qui sont une sorte de jeu de mots sur la magie de la chose, où il est généralement nécessaire de lever la main et de dire quelques mots magiques dans le bon ordre. pour obtenir un certain type de sortilège. L'ancienne méthode de l'API pour parler aux objets COM était très répandue. Vous deviez passer à travers de nombreuses étapes afin de persuader le compilateur de compiler le code pour vous.

Avant la version 4.0, les choses se détérioraient encore davantage si vous essayez de parler à un objet COM pour lequel vous n'avez pas d'interface ou de classe. Tout ce que vous avez est une référence IDispatch.

Si vous ne savez pas ce que c'est, IDispatch est fondamentalement une réflexion pour les objets COM. Avec une interface IDispatch, vous pouvez demander à l'objet "quel est le numéro d'identification de la méthode appelée Save", et construire des tableaux d'un certain type contenant les valeurs d'argument et enfin appeler une méthode Invoke sur l'interface IDispatch pour appeler la méthode, en transmettant toutes les informations que vous avez réussi à saisir.

La méthode Save ci-dessus pourrait ressembler à ceci (ce n'est certainement pas le bon 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, ...);

Tout cela pour simplement ouvrir un document.

VB avait des arguments optionnels et un support pour la plupart de ces éléments prêts à l'emploi il y a longtemps, donc ce code C #:

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

est fondamentalement juste C # rattrapant VB en termes d'expressivité, mais le fait de la bonne façon, en le rendant extensible, et pas seulement pour COM. Bien sûr, cela est également disponible pour VB.NET ou tout autre langage construit au-dessus du runtime .NET.

Vous pouvez trouver plus d'informations sur l'interface IDispatch sur Wikipedia: IDispatch si vous voulez en savoir plus à ce sujet. C'est vraiment triste.

Cependant, si vous vouliez parler à un objet Python? Pour cela, il existe une API différente de celle utilisée pour les objets COM, et comme les objets Python sont également de nature dynamique, vous devez faire appel à la magie de réflexion pour trouver les méthodes, les paramètres, etc. Mais pas la réflexion .NET, quelque chose d’écrit pour Python, assez semblable au code IDispatch ci-dessus, tout à fait différent.

Et pour Ruby? Une autre API encore.

JavaScript? Même affaire, API différente pour cela aussi.

Le mot clé dynamique est composé de deux choses:

  1. Le nouveau mot-clé en C #, dynamic
  2. Un ensemble de classes d'exécution qui sait comment traiter les différents types d'objets, qui implémente une API spécifique requise par le mot clé dynamic et qui mappe les appels avec la bonne façon de faire les choses. L'API est même documentée. Par conséquent, si vous avez des objets provenant d'un environnement d'exécution non couvert, vous pouvez les ajouter.

Le mot clé dynamic ne doit toutefois remplacer aucun code .NET existant. Bien sûr, vous pouvez le faire, mais cela n’a pas été ajouté pour cette raison, et les auteurs du langage de programmation C # avec Anders Hejlsberg à l’avant, ont Ils tiennent beaucoup à ce qu'ils considèrent toujours le C # comme un langage fortement typé et ne sacrifieront pas ce principe.

Cela signifie que même si vous pouvez écrire du code comme ceci:

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

et le compiler, il ne s’agissait pas d’une sorte de système magique permettant de comprendre ce que vous vouliez dire au moment de l’exécution.

Le but était de faciliter la communication avec d'autres types d'objets.

Il existe de nombreux documents sur Internet concernant le mot clé, les promoteurs, les adversaires, les discussions, les discours, les louanges, etc.

Je vous suggère de commencer avec les liens suivants, puis Google pour plus d'informations:

196

Je suis surpris que personne ne mentionne envoi multiple . Le moyen habituel de contourner ce problème est d'utiliser modèle de visiteur , ce qui n'est pas toujours possible. Vous obtenez donc des contrôles empilés is.

Voici donc un exemple concret d’une application personnelle. Au lieu de faire:

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

Tu fais:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

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

Notez que dans le premier cas, ElevationPoint est une sous-classe de MapPoint et si elle n’est pas placée avant MapPointelle ne sera jamais atteint. Ce n'est pas le cas avec dynamique, car la méthode de correspondance la plus proche sera appelée.

Comme vous pouvez le deviner à partir du code, cette fonctionnalité est très utile lorsque je traduisais des objets ChartItem vers leurs versions sérialisables. Je ne voulais pas polluer mon code avec les visiteurs et je ne voulais pas aussi polluer mes objets ChartItem avec des attributs inutiles spécifiques à la sérialisation.

23

Cela facilite l’interopérabilité des langages à typage statique (CLR) avec les langages dynamiques (python, Ruby ...) exécutés sur le DLR (Dynamic Langage Runtime), voir MSDN =:

Par exemple, vous pouvez utiliser le code suivant pour incrémenter un compteur en XML en C #.

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

En utilisant le DLR, vous pouvez utiliser le code suivant à la place pour la même opération.

scriptobj.Count += 1;

MSDN énumère ces avantages:

  • Simplifie le portage de langages dynamiques vers le .NET Framework
  • Active les fonctionnalités dynamiques dans les langues statiques
  • Offre des avantages futurs du DLR et du .NET Framework
  • Permet le partage de bibliothèques et d'objets
  • Fournit une distribution et une invocation dynamiques rapides

Voir MSDN pour plus de détails.

11
Philip Daubmeier

Un exemple d'utilisation:

Vous utilisez beaucoup de classes qui ont une propriété commune '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; }

}

Si vous écrivez une méthode commun qui récupère la valeur de la propriété 'CreationDate', vous devrez utiliser la réflexion:

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

Avec le concept 'dynamique', votre code est beaucoup plus élégant:

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

COM interop. Surtout IUnknown. Il a été conçu spécialement pour cela.

3
Aen Sidhe

Le meilleur cas d'utilisation de variables de type 'dynamiques' était pour moi lorsque, récemment, j'écrivais une couche d'accès aux données dans ADO.NET (à l'aide de SQLDataReader) et que le code appelait les procédures stockées héritées déjà écrites. . Il existe des centaines de procédures stockées héritées contenant la majeure partie de la logique métier. Ma couche d'accès aux données devait renvoyer une sorte de données structurées à la couche de logique métier, basée sur C #, pour effectuer certaines manipulations (même s'il n'y en a presque pas). Chaque procédure stockée renvoie un ensemble de données différent (colonnes de table). Ainsi, au lieu de créer des dizaines de classes ou de structures pour contenir les données renvoyées et les transmettre à la BLL, j’ai écrit le code ci-dessous, qui a l’air assez élégant et soigné.

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
user1451111

Il sera principalement utilisé par RAD et Python victimes pour détruire la qualité du code, IntelliSense et détecter un bogue lors de la compilation.

1
stormianrootsolver

Un autre cas d'utilisation du typage dynamic concerne les méthodes virtuelles qui rencontrent un problème de covariance ou de contravariance. Un de ces exemples est la fameuse méthode Clone qui renvoie un objet du même type que l'objet sur lequel il est appelé. Ce problème n'est pas complètement résolu avec un retour dynamique car il contourne la vérification de type statique, mais au moins vous n'avez pas besoin d'utiliser des conversions laides tout le temps comme avec plain object. Autrement, les castes deviennent implicites.

public class A
{
    // attributes and constructor here
    public virtual dynamic Clone()
    {
        var clone = new A();
        // Do more cloning stuff here
        return clone;
    }
}

public class B : A
{
    // more attributes and constructor here
    public override dynamic Clone()
    {
        var clone = new B();    
        // Do more cloning stuff here
        return clone;
    }
}    

public class Program
{
    public static void Main()
    {
        A a = new A().Clone();  // No cast needed here
        B b = new B().Clone();  // and here
        // do more stuff with a and b
    }
}
0
Frederic
  1. Vous pouvez appeler des langages dynamiques tels que CPython à l'aide de pythonnet:

dynamic np = Py.Import("numpy")

  1. Vous pouvez convertir les génériques en dynamic lorsque vous leur appliquez des opérateurs numériques. Ceci fournit une sécurité de type et évite les limitations des génériques. Ceci est essentiellement * la frappe de canard:

T y = x * (dynamic)x, où typeof(x) is T

0
denfromufa