[英]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.