Matthieu Mezil
C# 4 : dynamic
Avec C#4, apparaît un nouveau mot clé : dynamic. Comment peut-on l’utiliser pour simplifier nos codes ?
Par Matthieu Mezil publié le 28/10/2008 à 00:57, lu 4599 fois, 4 pages
 3 | Exemple de la « vraie vie »
Reprenons l’exemple du code de Philippe qui souhaite développer un TryGet valable pour les SPListCollection, SPUsers, SPWebs, etc. :
Toutes ces classes héritent de SPBaseCollection et sont toutes indexable par un string mais cette indexation est définie dans les classes filles.
Lors de la compilation, l’indexation est remplacée par une méthode get_Item.
Le code suivant proposé par Philippe :

private static Dictionary<Type, MethodInfo> s_GetItemMethodInfos = new Dictionary<Type, MethodInfo>();

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    try

    {

        MethodInfo methodInfo = null;

        Type spCollectionType = spBase.GetType();

        if (s_GetItemMethodInfos.ContainsKey(spCollectionType) == false)

        {

            methodInfo = spCollectionType.GetMethod("get_Item",

                            BindingFlags.InvokeMethod | BindingFlags.ExactBinding | BindingFlags.Instance |

                            BindingFlags.Public | BindingFlags.DeclaredOnly,

                            null,

                            new Type[] { typeof(string) },

                            null);

            s_GetItemMethodInfos.Add(spCollectionType, methodInfo);

        }

        else

            methodInfo = s_GetItemMethodInfos[spCollectionType];

 

        if (methodInfo == null)

            return false;

 

        result = (T)methodInfo.Invoke(spBase, new object[] { name });

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

Pourrait être réécrit en C#4 comme ceci :

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    dynamic dynamicSpBase = spBase;

    try

    {

        result = dynamicSpBase.get_Item(name);

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

// Il faudrait tester que ex.InnerException n’est pas nul dans le catch mais ce n’est pas le sujet.
L’utilisation de dynamic réduit donc considérablement le code et le simplifie.
On pourra cependant regretter le fait de ne pas pouvoir directement écrire :

result = (T)dynamicSpBase[name];

Mais il faut rappeler que ce n’est que la première CTP…
A noter que l’exception générée si la méthode n’existe pas est une Microsoft.CSharp.RuntimeBinder.RuntimeBinderException.
Le code précédent réellement exécuté est le suivant :

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    object dynamicSpBase = spBase;

    try

    {

        if (<TryGet>o__SiteContainer0<T>.<>p__Site1 == null)

        {

            <TryGet>o__SiteContainer0<T>.<>p__Site1 = CallSite<Func<CallSite, object, T>>.Create(

                new CSharpConversionPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),

                                            typeof(T),

                                            CSharpConversionPayload.ConversionKindEnum.ImplicitConversion));

        }

        if (<TryGet>o__SiteContainer0<T>.<>p__Site2 == null)

        {

            <TryGet>o__SiteContainer0<T>.<>p__Site2 = CallSite<Func<CallSite, object, string, object>>.Create(

                new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),

                                      false,

                                      false,

                                      "get_Item",

                                      typeof(object),

                                      null));

        }

        result = <TryGet>o__SiteContainer0<T>.<>p__Site1.Target(

            <TryGet>o__SiteContainer0<T>.<>p__Site1,

            <TryGet>o__SiteContainer0<T>.<>p__Site2.Target(

                <TryGet>o__SiteContainer0<T>.<>p__Site2,

                dynamicSpBase,

                name));

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(string.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

result sera affecté par le cast en T (CSharpConversionPayload) de l’appel à la méthode get_Item (CSharpCallPayload).
 
» Démarrer une discussion