Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List of generic interface with generic interface parameter

I know similar questions have been asked, but I didn't find any which was similar enough to what I did.

Let's say I have this:

public interface IData
{
    string Data { get; set; }
}
public interface IJob<out T> where T: IData
{
    T JobData { get; } // works because no setter

    void Run();
}

public class JobAData : IData
{
    public string Data { get; set; }
}

public class JobA : IJob<JobAData>
{
    public JobAData JobData { get; private set; } // implements IJob's get plus a set

    public JobA(JobAData data)
    {
        JobData = data;
    }

    public void Run()
    {
        //can use JobData nicely here
    }
}

And, because of the out parameter, this also works:

List<IJob<IData>> jobs = new List<IJob<IData>>();
jobs.Add(new JobA(new JobAData()));

//in another class, extremely simplified (actually running with Quartz)
foreach (var job in jobs)
{
    job.Run();
}

While this works fine, it feels like a hack since I have to remember that JobA needs a setter that is not enforced by the interface.
I originally was using a double IJob interface (an IJob and an IJob<T>) but that meant I had to cast from IJob<T> to IJob and I didn't like that.
Is there any cleaner way to do this?

like image 796
Camilo Terevinto Avatar asked Feb 22 '26 16:02

Camilo Terevinto


1 Answers

UPDATE

My original suggestion was to create an abstract class that sets the Data in the constructor,

public abstract class JobBase<T> : IJob<T> where T : IData {

    public JobBase(T data) {
        JobData = data;
    }

    public T JobData { get; private set; }

    public abstract void Run();
}

forcing derived classes to set the JobData property.

public class JobA : JobBase<JobAData> {
    public JobA(JobAData data) : base(data) { }

    public void Run() {
        //can use JobData nicely here
    }
}

ORIGINAL ANSWER

Following the abstract base class idea consider a abstract factory method that would force any derived class to provide data, either in the property itself

public abstract class JobBase<T> : IJob<T> where T : IData {
    public T JobData { get { return GetData(); } }

    public abstract void Run();

    public abstract T GetData();
}

or having a private setter and setting it one time in the constructor

public abstract class JobBase<T> : IJob<T> where T : IData {

    public JobBase() {
        JobData = GetData();
    }

    public T JobData { get; private set; }

    public abstract void Run();

    public abstract T GetData();
}

Any derived implementations would be forced to implement the GetData method.

like image 129
Nkosi Avatar answered Feb 24 '26 16:02

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!