我想要一堆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
。接下来,我尝试使用泛型进行重载(我将string
和int
更改为其他类型,因为此技巧对密封类无效)
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#方法从异步重载中获取不同的数据类型?如果需要的话,我会将数据类型合并到函数名称中,而不是重载,但这似乎是多余的。而且由于这些函数抽象了数据库调用,所以其中某些确实具有很长的名称! (这些函数通常将有多个调用方,因此需要集中定义而不是在调用它们的类中进行定义。)
答案 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);