słowo kluczowe dynamic i mechanizm late binding

0

Mam pytanie: jak przy pomocy późnego wiązania i słowa kluczowego dynamic skorzystać ze statycznej metody w klasie?

  public class MathClass
    {
        public static int Add(int x, int y)
        {
            return x + y;
        }
    }
  Type m = asm.GetType("Math.MathClass");
                             

                dynamic dynm = Activator.CreateInstance(m);
                Console.WriteLine(dynm.Add(10, 10));

To coś co widać wyżej wyrzuca wyjątek, no chyba ze usunę to "static" z metody.

1

Pierwsze rozwiązanie (dość brzydkie niestety) jakie mi przychodzi do głowy:

Console.WriteLine(dynm.GetType().GetMethod("Add").Invoke(null, new object[] { 10, 10 }));

Można to lekko poprawić tworząc metodę pomocniczą:

static class StaticHelper {
    public static object InvokeStatic(this object obj, string method, params object[] args) {
        return obj.GetType().GetMethod(method).Invoke(null, args);
    }
}

object obj = dynm; // już nie potrzeba dynamic
Console.WriteLine(obj.InvokeStatic("Add", 10, 10));

Poszukałem rad u mądrych w sieci, znalazłem ciekawe rozwiązanie (wersja praktycznie oryginalna, zmiany głównie w formatowaniu):
http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

public class StaticMembersDynamicWrapper : DynamicObject {
    Type type;

    public StaticMembersDynamicWrapper(Type type) {
        this.type = type;
    }

    // Handle static properties
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        PropertyInfo prop = type.GetProperty(binder.Name, 
            BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);

        if (prop == null) {
            result = null;
            return false;
        }

        result = prop.GetValue(null, null);
        return true;
    }

    // Handle static methods
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        MethodInfo method = type.GetMethod(binder.Name,
            BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);

        if (method == null) {
            result = null;
            return false;
        }

        result = method.Invoke(null, args);
        return true;
    }
}

Działa to tak:

dynamic dynm = Activator.CreateInstance(m);
dynamic staticDynm = new StaticMembersDynamicWrapper(dynm.GetType());
Console.WriteLine(staticDynm.Add(10, 10));

Tzn. otrzymujesz dynamiczny obiekt za pomocą którego możesz wołać metody statyczne z klasy. Niestety, dla odmiany nie możesz wołać metod niestatycznych (cały wrapper działa na poziomie typów)...

Moja próba modyfikacji, która UMnieDziała(tm):

public class StaticWrapper : DynamicObject {
    object obj;

    public StaticWrapper(object wrapped) {
        this.obj = wrapped;
    }

    // Handle static properties
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        PropertyInfo prop = obj.GetType().GetProperty(binder.Name, 
            BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);

        if (prop == null) {
            result = null;
            return false;
        }

        result = prop.GetValue(obj, null);
        return true;
    }

    // Handle static methods
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        MethodInfo method = obj.GetType().GetMethod(binder.Name,
            BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);

        if (method == null) {
            result = null;
            return false;
        }

        result = method.Invoke(obj, args);
        return true;
    }
}

Użycie:

dynamic dynm = new StaticWrapper(Activator.CreateInstance(m));
Console.WriteLine(staticDynm.Add(12, 10));

Do wyboru, do koloru.

0

@msm
Przypadek pierwszy jest ciekawy jako tyle, że i tak wykonujemy tam to Invoke, którego przy niestatycznych metodach nie trzeba (chyba, że nie korzystamy z dynamic).
Co do pozostałych dwóch przypadków to może lepiej nie korzystać z dynamic a zrobić to metodą "klasycznej" refleksji bo to w przypadku mojego programu i tak jest mniej kodu a słowo dynamic miało go zmniejszyć a nie zwiększyć więc to co zaproponowałeś raczej nie jest oczekiwanym przeze mnie rezultatem.
Tak czy inaczej dzieki :)

0

Type m = asm.GetType("Math.MathClass");
Jeżeli już masz Type, na którym bezpośrednio możesz wykonać InvokeMember, to do czego ci właściwie dynamic?

Console.WriteLine(m.InvokeMember("Add", BindingFlags.InvokeMethod, null, null, new object[]{10,10}));
1

@mvt8 - pozostałe dwa jako ciekawostka, jak można podejść do rozszerzania dynamika. Raczej widać że nie ułatwiają rozwiązania tylko utrudniają, ale może komuś (jak mi) się spodoba.

Ad. reszty:

(O metodach statycznych przez dynamic) So I then posted the question on our internal C# list, and got a reply from C# guru Eric Lippert, basically saying that it was a potentially interesting idea but was just not supported in C# 4.0. Fair enough, this is only the beginning of C# dynamic, and it doesn’t do everything.

@Azarien - prawie dokładnie to samo co moje pierwsze, tylko zapomniałem o InvokeMember i robiłem GetMethod().Invoke() (ale wychodzi podobna długośc w sumie i tak).

0

@Azarien @msm
To po co w takim razie późne wiązanie skoro mamy to InvokeMembers?
Myślałem ze można to zrobić tylko przy pomocy klasy System.Activator a tu psikus ;P

0

Never mind, głupi jestem :P

1 użytkowników online, w tym zalogowanych: 0, gości: 1