Reputation: 32068
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
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
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