簡體   English   中英

用 generics 鑄造

[英]Type casting with generics

我為調度作業創建了以下抽象:

public abstract class BaseJob
{
    public string? JobId { get; set; }
}

public interface IJobData
{ }

public interface IJob<in TJobData> where TJobData : IJobData
{
    Task ExecuteAsync(TJobData jobData);
}

我使用工廠創建工作:

public class JobCreator<TJob, TJobData>
    where TJob : IJob<TJobData>, new()
    where TJobData : IJobData
{
    public async Task ExecuteAsync(TJobData jobData)
    {
        var job = new TJob();
        await job.ExecuteAsync(jobData);
    }
}

作業安排如下:

JobClient.Enqueue<JobCreator<ForgotPasswordJob, ForgotPasswordJobData>>(job => job.ExecuteAsync(jobData));

調度程序會將作業脫水到作業存儲並稍后對其進行水化。 我可以進入水化過程並打印類型:

Application.Common.Interfaces.JobCreator`2[Application.Jobs.ForgotPasswordJob,Application.Jobs.ForgotPasswordJobData]

在鈎子里面,我可以進行補水工作

var job = schedulerHydrationMagic...

此時變量作業為 object 類型。 是否可以投射它,以訪問 JobCreator 甚至更好的 ForgotPasswordJob? 我想在 BaseJob 中設置 JobId。

我試過了

var job = schedulerMagic as JobCreator<IJob<IJobData>, IJobData>

但類型檢查抱怨 IJob 必須是非抽象類型。

我想您需要一個進一步的接口才能從調度程序中獲取作業。 不能使用工廠接口,它有new約束。

JobCreator對通用TJob參數有一個通用的new()約束:

public class JobCreator<TJob, TJobData>
    where TJob : IJob<TJobData>, new() // <- here
    where TJobData : IJobData
{
    public async Task ExecuteAsync(TJobData jobData)
    {
        var job = new TJob();
        await job.ExecuteAsync(jobData);
    }
}

很明顯,您這樣做是為了調用new TJob()

但它解釋了為什么第一個泛型參數 - TJob - 必須始終是非抽象類型。

如果 TJobData 的泛型類型是TJobData IJob<IJobData> ,那么在運行時該行代碼將等同於

var job = new IJob<IJobData>();

......這沒有意義。 您不能在接口(或抽象 class)上調用new 。這對我們來說很難發現,但對編譯器來說很容易。

強類型代碼的重點是編譯器在編譯應用程序時會檢測到這樣的事情。 否則代碼看起來沒問題,你可以運行它,然后在運行時它發現TJob沒有默認構造函數。 最好讓編譯器拒絕它並讓我們弄清楚,而不是認為它沒問題,然后得到一個更令人困惑的運行時錯誤。


對此沒有簡單的答案,因為問題就在設計的中間。 這就是我所說的瘋狂兔子洞 這不是貶義詞——我自己做過。

我的第一個建議不是弄清楚如何使 generics 工作——而是停止嘗試做你正在做的事情。 真的有很多可能的實現嗎? 從代碼看起來只有一個。 因此,通過嘗試編寫一個通用版本來處理未知的未來類型,您可能會嘗試解決您沒有的問題。 在這種情況下,答案很簡單。 不要試圖解決它。 只需編寫代碼來安排您需要安排的任何事情,而不需要所有 generics。

如果您必須這樣做,那么您需要擺脫new()約束。 這意味着您將無法調用new TJob() 相反,您需要創建某種工廠接口,例如:

public interface IJobFactory
{
    IJob<TJobData> Create<TJobData>() where TJobData : IJobData
}

然后你的JobCreator看起來像這樣:

public class JobCreator<TJob, TJobData>
    where TJob : IJob<TJobData>
    where TJobData : IJobData
{
    private readonly IJobFactory _jobFactory;

    public JobCreator(IJobFactory jobFactory)
    {
        _jobFactory = jobFactory;
    }

    public async Task ExecuteAsync(TJobData jobData)
    {
        var job = _jobFactory.Create<TJobData>();
        await job.ExecuteAsync(jobData);
    }
}

但這並沒有解決問題。 它只是移動了它。 現在你必須弄清楚如何實現IJobFactory (這已經令人困惑了,因為現在JobCreator並沒有真正創建工作 - IJobFactory可以。)

這就是為什么我稱它為兔子洞。 它只會永遠持續下去。 如果有任何方法可以修改代碼以擺脫問題而不是嘗試解決問題,那就更好了。

一旦你得到了你需要工作的代碼,沒有所有的混亂,一個更大的問題的更好的解決方案 - 如果甚至有問題 - 可能會成為焦點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM