dotnet / dotnet/dotnet-api-docs
Assembly.GetCallingAssembly should contain info for async methods
- Dominant language
- C#
- Stars
- 949
- Forks
- 1.7k
- Avg merge
- 3d 27m
- Merged PRs (30d)
- 49
Description
I think we should add another section to the remarks of [Assembly.GetCallingAssembly](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.getcallingassembly). This section should contain info about `async` methods and how they affect `GetCallingAssembly`.
Consider the following unit test:
```csharp
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace AssemblyTests
{
public class AsyncGetCallingAssembly
{
public AsyncGetCallingAssembly(ITestOutputHelper output) => Output = output;
private ITestOutputHelper Output { get; }
[Fact]
public async Task GetCallingAssemblyFromAsyncMethod()
{
var returnedAssembly = await DoSomethingAsync();
Output.WriteLine(returnedAssembly.ToString());
var expectedAssembly = typeof(AsyncGetCallingAssembly).Assembly;
Assert.NotEqual(expectedAssembly, returnedAssembly);
}
private static async Task DoSomethingAsync()
{
var callingAssembly = Assembly.GetCallingAssembly();
await Task.Delay(50);
return callingAssembly;
}
}
}
```
Intuitively, I would expected that `Assembly.GetCallingAssembly()` in method `DoSomethingAsync` returns the same assembly as the calling unit test method. However, an async method in .NET is always transformed to a struct (class in Debug mode) that implements `IAsyncStateMachine`. And its `MoveNext` method is always called by `AsyncTaskMethodBuilder`, which resides in `System.Private.CoreLib` for .NET 5:

This could be circumvented by the following pattern: the public API surface returns a `Task` or `Task`, but it is not `async`. In this context, we can safely call `Assembly.GetCallingAssembly` and then call an internal method that is actually async, like shown in the following modification of the above example:
```csharp
public class AsyncGetCallingAssembly
{
public AsyncGetCallingAssembly(ITestOutputHelper output) => Output = output;
private ITestOutputHelper Output { get; }
[Fact]
public async Task GetCallingAssemblyFromAsyncMethod()
{
var returnedAssembly = await DoSomethingAsync();
Output.WriteLine(returnedAssembly.ToString());
var expectedAssembly = typeof(AsyncGetCallingAssembly).Assembly;
Assert.Equal(expectedAssembly, returnedAssembly);
}
private static Task DoSomethingAsync()
{
var callingAssembly = Assembly.GetCallingAssembly();
return DoSomethingInternal(callingAssembly);
static async Task DoSomethingInternal(Assembly callingAssembly)
{
await Task.Delay(50);
return callingAssembly;
}
}
}
```
For the docs, we should probably find another example that is more appropriate than my unit test here. What do you guys think about it?
Contributor guide
Assessment
This issue has not been assessed yet.