方法返回泛型委托时返回泛型类型

huangapple go评论81阅读模式
英文:

Method to return a generic type when passing in a generic delegate

问题

I understand your request to provide translations for the code portions while excluding other content. Here are the code portions translated into Chinese:

// 我想要:
// 1. 返回一个通用类型,可以是 ICollection 或字符串,需要灵活性。这将由消耗 API 的 REST API 客户端返回。(不同的端点具有不同的返回类型)

// 2. 传递包含要从实际 REST 客户端调用的方法的委托。
// REST 客户端是一个名为 IClient 的接口,可以像这样解析,它只是应用程序的 API 客户端的实例。

public static IClient GetClient()
{
    return App.Current.Handler.MauiContext.Services.GetService<IClient>();
}

// 然后,您可以像这样访问 API(InjectedApiClient 是一个静态类)

var client = InjectedApiClient.GetClient();
var languages = await client.GetAllLanguagesAsync(); // 从 API 获取所有语言 ICollection<Language> 返回类型

// 在过去,我记得使用过这样的委托:
public async Task DoSomething(Func<Task> methodToExecute)
{
    // 这里有一些通用逻辑,可以应用于所有情况
    await methodToExecute();
    // 在完成或失败后可以应用一些通用逻辑
}

public async Task FlagOrder(Guid orderId)
{
    await DoSomething(async () => await SetOrderStatusToFlagged(orderId));
}
// 我试图在设计以下逻辑时使用相同的方法:
// 这个方法应该接受不需要重试的任何 API 调用,并且灵活返回任何类型
public static async Task<T> ExecuteCall<T>(Func<Task<T>> methodToExecute)
{
    try
    {
        return await methodToExecute();
    }
    catch (HttpRequestException)
    {
        return default(T);
        // 将此记录到队列以后处理
    }
}

// 这个方法应该接受需要重试的任何 API 调用,并且灵活返回任何类型
public static async Task<T> ExecuteWithRetry<T>(Func<Task<T>> methodToExecute)
{
    try
    {
        return await _retryPolicy.ExecuteAsync(async () =>
        {
            return await methodToExecute();
        });
    }
    catch (HttpRequestException)
    {
        return default(T);
        // 将此记录到队列以后处理
    }
}

// 下面的代码有效,但我不喜欢我需要为应用程序中的每个调用调用 GetClient 并像这样传递它:

var client = InjectedApiClient.GetClient();
var machines = await InjectedApiClient.ExecuteWithRetry(async () => await
client.GetAllMachinesAsync());
var languages = await InjectedApiClient.ExecuteWithRetry(async () => await
client.GetAllLanguagesAsync());

// 是否有任何方法可以做到以下:

public static async Task<T> ExecuteCall<T>(Func<Task<T>> methodToExecute)
{
    try
    {
        // 我想在这里获取客户端,这样就不需要在每个地方都实例化它,也不想将其作为参数传递,
        // 但是要使用此客户端在这里执行传递的方法(methodToExecute)
        var client = InjectedApiClient.GetClient();

        return await // 嘿客户端,获取传递的委托(methodToExecute)并执行它,然后返回其返回值。
    }
    catch (HttpRequestException)
    {
        return default(T);
        // 将此记录到队列以后处理
    }
}

// 当我将客户端作为参数传递时,此重构将失去其目的。我不想为每个单独的 API 调用执行方法,只需一个并重用它用于所有操作。
// 此外,在此方法内部调用获取客户端将仅允许我手动选择要从 IClient 调用的方法,因此再次需要多个方法,因此通用性将无效。我只想有两种方法来处理任何可能的结果。

希望这些翻译对您有所帮助!如果您需要更多帮助,请随时提出。

英文:

I want to:

  1. return a generic type, can be for example ICollection OR string, needs to be flexible. this will be returned by the restapi client that consumes the api.(different endpoints have different return types)

  2. pass in some kind of delegate containing the method to be called from the actual rest client.

rest client is an Interface IClient that is resolved like this, it is just and instance of the api client for the application.

 public static IClient GetClient()
        {
            return App.Current.Handler.MauiContext.Services.GetService&lt;IClient&gt;();
        }

you can then access api like this (InjectedApiClient is a static class)

var client = InjectedApiClient.GetClient();
var languages = client.GetAllLanguagesAsync()) // get all languages from the api ICollection&lt;Language&gt; return type

In the past I remember using delegates like this:

  public async Task DoSomething(Func&lt;Task&gt; methodToExecute)
        {
		//some generic logic here that can be applied to everything
                await methodToExecute();
		//some generic logic here that can be applied once this is done or failed
        }

     public async Task FlagOrder(Guid orderId)
        {
            await DoSomething(async () =&gt; await SetOrderStatusToFlagged(orderId));
        }

I was trying to use the same approach when designing the following logic:

//this method should accept any api call that does not need retrying and be flexible to return any type
     public static async Task&lt;T&gt; ExecuteCall&lt;T&gt;(Func&lt;Task&lt;T&gt;&gt; methodToExecute)
        {
            try
            {
                return await methodToExecute();
            }
            catch (HttpRequestException)
            {
                return default(T);
                //log this to queue to do
            }

        }

//this method should accept any api call that needs retrying and be flexible to return any type
        public static async Task&lt;T&gt; ExecuteWithRetry&lt;T&gt;(Func&lt;Task&lt;T&gt;&gt; methodToExecute)
        {
            try
            {
                return await _retryPolicy.ExecuteAsync(async () =&gt;
                {
                    return await methodToExecute();
                });
            }
            catch (HttpRequestException)
            {
                return default(T);
                //log this to queue to do
            }
        }

the following code works but I dont like the fact I need to call GetClient and pass it like this for every call in the app:

var client = InjectedApiClient.GetClient();
var machines = await InjectedApiClient.ExecuteWithRetry(async () =&gt; await 
client.GetAllMachinesAsync());
var languages = await InjectedApiClient.ExecuteWithRetry(async () =&gt; await 
client.GetAllLanguagesAsync());

Is there any way I could do the following:

 public static async Task&lt;T&gt; ExecuteCall&lt;T&gt;(Func&lt;Task&lt;T&gt;&gt; methodToExecute)
    {
        try
        {
	//I want to get the client here so dont need to instantiate that everywhere I am 
    //making api calls, also dont want to pass it as an argument 
	// but execute the method passed down as a methodToExecute here with this client

	 var client = InjectedApiClient.GetClient();

            return await //hey client take the passed delegate(methodToExecute) and execute that and return whatever it returns.
        }
        catch (HttpRequestException)
        {
            return default(T);
            //log this to queue to do
        }
    }

When I pass the client as an argument this refactoring is loosing its purpose I dont want to
have execute method for each individual api call just one and reuse it for everything.
Also calling get client inside this method will only allow me to manually choose which method to call from the Iclient so again will need multiple methods so the generic would be useless. I want to have only 2 methods for any possible outcome.

答案1

得分: 3

看起来你需要接受Func&lt;IClient, Task&lt;T&gt;&gt;(参见Func&lt;T,TResult&gt;委托),即类似以下的内容:

public static async Task&lt;T&gt; ExecuteCall&lt;T&gt;(Func&lt;IClient, Task&lt;T&gt;&gt; methodToExecute)
{
    try
    {
        var client = InjectedApiClient.GetClient();

        return await methodToExecute(client);
    }
    catch (HttpRequestException)
    {
        return default(T);
        //将此记录到队列以后处理
    }
}

用法如下:

var machines = await InjectedApiClient.ExecuteCall(c =&gt; c.GetAllMachinesAsync());

你还可以完全泛型化:

public async Task&lt;TResult&gt; ExecuteCall&lt;T, TResult&gt;(Func&lt;T, Task&lt;TResult&gt;&gt; method)
{
    T t = ...;
    return await method(t);
}

带有更复杂用法的情况(另一种选择 - 指定两个泛型参数):

await InjectedApiClient.ExecuteCall((IClient c) =&gt; c.GetAllMachinesAsync());
英文:

It seems that you need to accept Func&lt;IClient, Task&lt;T&gt;&gt; (see the Func&lt;T,TResult&gt; delegate), i.e. something along these lines:

public static async Task&lt;T&gt; ExecuteCall&lt;T&gt;(Func&lt;IClient, Task&lt;T&gt;&gt; methodToExecute)
{
    try
    {
        var client = InjectedApiClient.GetClient();

        return await methodToExecute(client);
    }
    catch (HttpRequestException)
    {
        return default(T);
        //log this to queue to do
    }
}

And usage:

var machines = await InjectedApiClient.ExecuteCall(c =&gt; c.GetAllMachinesAsync());

You also can go full generic with:


public async Task&lt;TResult&gt; ExecuteCall&lt;T, TResult&gt;(Func&lt;T, Task&lt;TResult&gt;&gt; method)
{
    T t = ...;
    return await method(t);
}

With a bit more convoluted usage (another option - specify both generic parameters):

await InjectedApiClient.ExecuteCall((IClient c) =&gt; c.GetAllMachinesAsync());

huangapple
  • 本文由 发表于 2023年7月13日 20:39:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679488.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定