如何使用C#异步方法重载并返回多种类型?

时间:2018-07-12 10:03:00

标签: c# async-await overloading

我想要一堆GetDataAsync()方法,这些方法被重载以根据所需的类型返回各种数据集。如果函数不是异步的,则可以基于out参数进行重载:

public void GetData(int userId, out int age, out string name) { ... }
public void GetData(int userId, out string name, out string address) { ... }

但是out方法不允许使用async。接下来,我尝试使用泛型进行重载(我将stringint更改为其他类型,因为此技巧对密封类无效)

public async Task<Tuple<TName, TAddress>> GetDataAsync<TName, TAddress>(int userId)
    where TAddress : Address where TName : Name
{ ... }
public async Task<Tuple<TAge, TName>> GetDataAsync<TAge, TName>(int userId)
    where TAge : Age where TName : Name
{ ... }

但是我收集的模板专业化在C#中实际上并不存在,因此它们被认为是相同的功能。

确实造成这种重载的丑陋黑客正在传入函数填充的缓冲区:

public async Task GetDataAsync(int userId, int[] id_output, string[] name_output)
{
    await Task.Delay(10);
    id_output[0] = 60;
    name_output[0] = "Freddy";
}

public async Task GetDataAsync(int userId, string[] name_output, string[] address_output)
{
    await Task.Delay(10);
    name_output[0] = "Freddy";
    address_output[0] = "Elm Street";
}

但是该黑客的API错误。 (使用列表作为缓冲区会更安全一些。)是否有任何适当的C#方法从异步重载中获取不同的数据类型?如果需要的话,我会将数据类型合并到函数名称中,而不是重载,但这似乎是多余的。而且由于这些函数抽象了数据库调用,所以其中某些确实具有很长的名称! (这些函数通常将有多个调用方,因此需要集中定义而不是在调用它们的类中进行定义。)

2 个答案:

答案 0 :(得分:1)

out参数是为返回多个值而创建的,但它们不适用于异步方法,因为实际方法在概念方法结束执行之前就返回了。

这就是为此语言添加tuples的原因。

组代表一组松散相关的值。它们无意代表数据库原始数据,实体或任何具有其自身含义/概念的东西。

数组也不是一个好选择,因为不能保证数组不能作为方法参数,为此,它们也不是强类型的。

您正在寻找的是generics

Task<Response> GetAsync<Request, Response>(TRequest request);

答案 1 :(得分:0)

类型安全和相对清洁的解决方案受到Paulo Morgado的回答的启发。 Request类包装了以前的参数,这些参数不能用于重载,因为它们始终是相同的-用户ID和令牌。在编译器眼中,未使用的类型参数仅用于区分Request<Foo, Bar>Request<Foo, Quux>

public class Request<T1, T2>
{
    // Common data needed by every GetAsync(), no matter what it returns:
    public string user;
    public string token;

    public Request(string user, string token)
    {
        this.user = user;
        this.token = token;
    }
}

public class Request<T1, T2, T3> : Request<T1, T2>
{
    public Request(string user, string token) : base(user, token) { }
}

然后可以重载方法,并且可以使每个方法返回与请求相对应的类型。 (这些功能必须手动编写,但是它们的使用是类型安全的。)

public async Task<Tuple<UserInventory, UserHeroSet>> LoadAsync(
    Request<ServerUserInventory, UserHeroSet> request)
{ ... }

public async Task<Tuple<UserInventory, BattleData>> LoadAsync(
    Request<UserInventory, BattleData> request)
{ ... }

使用方法如下:

var dbTuple = await LoadAsync(new Request<UserInventory, BattleData>(user, token)).ConfigureAwait(false);
dbTuple.Deconstruct(out UserInventory inventory, out BattleData battleData);