How to await an async private method invoked using reflection in WinRT?

24,595

Solution 1

Well you need to use the value returned by the method. Do you know the type? For example, if it's always a Task, you could use:

await (Task) objType.GetTypeInfo()
                    .GetDeclaredMethod("ThePrivateMethod")
                    .Invoke(theObject, null);

If you don't know the return type but know it will be awaitable, you could use dynamic typing:

await (dynamic) objType.GetTypeInfo()
                       .GetDeclaredMethod("ThePrivateMethod")
                       .Invoke(theObject, null);

I would try to avoid having to call a private method by reflection in your unit tests in the first place though. Can you test it indirectly via the public (or internal) API? That's generally preferable.

Solution 2

Invoke should return an object convertible to Task. Just await that.

If your private method returns void, then you'll need a custom SynchronizationContext, which is messy. It's better to have your methods return Task/Task<T>.

Share:
24,595
jokeefe
Author by

jokeefe

Updated on July 08, 2022

Comments

  • jokeefe
    jokeefe almost 2 years

    I'm writing unit tests for a WinRT app, and I am able to invoke non-async private methods using this:

    TheObjectClass theObject = new TheObjectClass();
    Type objType = typeof(TheObjectClass);
    objType.GetTypeInfo()
           .GetDeclaredMethod("ThePrivateMethod")
           .Invoke(theObject, null);
    

    However, if the private method in question is async, the code will continue execution without waiting for it to finish.

    How do I add await functionality to this?

  • svick
    svick over 11 years
    But cast to dynamic won't work if GetAwaiter() is an extension method, which is exactly the case for WinRT IAsyncAction.
  • jokeefe
    jokeefe over 11 years
    @Jon Skeet: Thanks! This did it. It was a Task object being returned, so all I needed to do was cast it and add the await. I also found that you need to make the unit test method's return type Task as well when you make it async or it won't be executed by the unit test frame work.
  • Jon Skeet
    Jon Skeet over 10 years
    @Jeff: It would help if you'd give more details, like the compiler error you're getting. It may well be worth creating a new question rather than having a comment discussion about it.
  • Jon Skeet
    Jon Skeet over 10 years
    @Jeff: That sounds like you're trying to use await without being in an async method (or lambda) - that has nothing to do with what you're trying to await.
  • Jeff
    Jeff over 10 years
    @JonSkeet I think you're right. I don't have the code in front of me, but I think it might have been inside an anonymous method that wasn't marked async. Sorry for the digression, I'll erase my comments.
  • Shimmy Weitzhandler
    Shimmy Weitzhandler over 8 years
    Is there a way to determine if that method is async or not?
  • Jon Skeet
    Jon Skeet over 8 years
    @Shimmy: No, and you shouldn't need to. That's just an implementation detail. You should know whether it will perform asynchronously or not, but a method can return a Task<T> and perform asynchronously without being implemented via async/await.
  • Shimmy Weitzhandler
    Shimmy Weitzhandler over 8 years
    Alight I got it now after experimenting with it a bit more. Thanks Jon! Do you ever Thread.Sleep()?
  • Atta H.
    Atta H. about 7 years
    It worked for me, though i added a slight modification into it for my need var instance = MyFactory.Instance.Create(myClassName); // custom factory var method = instance.GetType().GetMethod(myMethodName); await (dynamic) method.Invoke(instance, null);
  • ygoe
    ygoe almost 6 years
    When I try something like this, I get the compiler error: CS0656: Der vom Compiler angeforderte Member "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create" fehlt.
  • Jon Skeet
    Jon Skeet almost 6 years
    @ygoe: It sounds like your project may be messed up. It's hard to say more without a lot more information. I suggest you ask a new question.
  • ygoe
    ygoe almost 6 years
    @JonSkeet Maybe this only works in WinRT and not NETStd.
  • Jon Skeet
    Jon Skeet almost 6 years
    @ygoe: It works with .NET Standard projects running in regular .NET Core or .NET. Again, rather than trying to get the details in comments, I suggest you ask a new question with all those details.
  • Petr Voborník
    Petr Voborník about 5 years
    In Xamarin.Forms for UWP and Android it doesn't works.
  • Jon Skeet
    Jon Skeet about 5 years
    @PetrVoborník: I'm afraid with no more information than "it doesn't works" it's unlikely that anyone will be able to help you. I suggest you create a new question with a complete example, specific context (not just "Android" but which runtime you're using) and error message.