Camilo Terevinto
Camilo Terevinto

Reputation: 32068

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?

Upvotes: 3

Views: 1556

Answers (2)

Nkosi
Nkosi

Reputation: 247153

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.

Upvotes: 2

praty
praty

Reputation: 573

From what I understand, you want to enforce setter definition on inheritance which would have accessibility restriction as well! If you define a setter method, you would still end up making it publicly accessible. And, "double IJob interface (an IJob and an IJob<T>) but that meant I had to cast from IJob<T> to IJob" doesn't sound good to you.

There are not much solutions to this situation but one work around can be restriction using Abstract Classes. What I am suggesting here is something like this:

public interface IData
{
    string Data { get; set; }
}
public interface IJob<out T> where T : IData
{
    T JobData { get; }

    void Run();
}

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

public abstract class Abs_JobA : IJob<JobAData>
{
    public abstract JobAData JobData { get; protected set; }
    public abstract void Run();
}

public class JobA : Abs_JobA
{
    public override JobAData JobData
    {
        get;
        protected set;
    }

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

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

So now, you do not implement IJob to subsequent classes but, rather you extend Abs_JobA abstract class.

Upvotes: 1

Related Questions