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

2

71

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?

Acne answered 5/2, 2013 at 15:58 Comment(2)
Since this is a WinRT app, I have the feeling that reflection/invocation of private members is disallowed. I can't find official documentation of this on google right now, closest is: blogs.microsoft.co.il/blogs/sasha/archive/2011/09/17/… EDIT: These are unit tests though, so maybe it's a non-issue. :)Wat
@Chris Sinclair Actually, the code I have above works perfectly fine for private methods. My issue is specifically with asynchronous ones. The issue would apply to public methods invoked via reflection as well.Acne
M
118

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.

Marlborough answered 5/2, 2013 at 16:1 Comment(15)
But cast to dynamic won't work if GetAwaiter() is an extension method, which is exactly the case for WinRT IAsyncAction.Veal
@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.Acne
@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.Marlborough
@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.Marlborough
@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.Nucleolated
Is there a way to determine if that method is async or not?Mountainside
@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.Marlborough
Alight I got it now after experimenting with it a bit more. Thanks Jon! Do you ever Thread.Sleep()?Mountainside
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);Shellbark
When I try something like this, I get the compiler error: CS0656: Der vom Compiler angeforderte Member "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create" fehlt.Oldenburg
@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.Marlborough
@JonSkeet Maybe this only works in WinRT and not NETStd.Oldenburg
@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.Marlborough
In Xamarin.Forms for UWP and Android it doesn't works.Moxley
@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.Marlborough
S
9

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>.

Stylet answered 5/2, 2013 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.