简体   繁体   中英

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?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM