1

Is it possible to change specific type ex: ConcurentBag<T> to S type. I want to make generic class which loads data and reload source collection(list<T>, ConcurrentBag<T>, Dictionary)

This is my code:

public interface IRepositoryRelouder
{
    private void ReloadData();
}
public interface IRepositoryLoader<out T> : IRepositoryRelouder
{
    IEnumerable<T> LoadData();
}
public abstract class Test<T> : IRepositoryLoader<T>
{
    protected ConcurrentBag<T> Source;
    public abstract IEnumerable<T> LoadData();

    private void void IRepositoryRelouder.Reload() => 
        Source = new ConcurrentBag<T>(LoadData());
}

I would like to load and reload data in parent class and create child classes which have access to Source property. Is it passible to replace ConcurentBag to next generic parameter?

I wonder if it is possible something like this:

 public abstract class Test<T,S> : IRepositoryLoader<T>
    {
        protected S <T> Source;
        public abstract IEnumerable<T> LoadData();

        private void void IRepositoryRelouder.Reload() => 
            Source = new S <T>(LoadData());
    }

Where S is ConcurrentBag<T> or List<T>

Update

I thought to separate the reloading mechanism into the parents' classes. I can have two reloading mechanisms: simple and complex (invokes several methods inside the reload method). The children's classes have access to the repository and factories. There are several insert, create methods that use linqu on the data source from the inherited class in the child classes. The complex mechanism contains additional fields and methods, so I did not want to duplicate the code. What would be better to do in this situation? Can I use a dictionary that also implementsIEnumerable?

IRepositoryRelouder interface is only for call Reload method for ech classes that implements this interface in configuration class.

dMilan
  • 257
  • 3
  • 13
  • What exactly do you want to do? Can you provide a pseudo code example maybe? – JSteward Jul 23 '18 at 19:37
  • I wasn't able to understand the question. Could you please restate? What is wrong with your example code? (Other than Test.Load should be Test.LoadData) – Eren Ersönmez Jul 23 '18 at 19:39
  • 1
    I don't think what you're going for is possible, granted it's still unclear. But you wouldn't be able to declare two `TestA` like that one would have to be `TestB`. And generic types can't be replaced at runtime. – JSteward Jul 23 '18 at 19:53
  • This question is very unclear. From the looks of it you just want to be able to switch the ConcurrentBag List to be a Concurrent bag, but from the sounds like you want to do something totally different – johnny 5 Jul 23 '18 at 20:01
  • Is it clear now? – dMilan Jul 23 '18 at 20:10
  • @dMilan yeah, see my answer, its possible, but it seems like you can avoid the problem by just making the reload method abstract – johnny 5 Jul 23 '18 at 20:31
  • @dMilan does that answer your question? – johnny 5 Jul 23 '18 at 21:17
  • @johnny 5, did you see updates? – dMilan Jul 24 '18 at 07:48
  • @John Wu did you see updates? – dMilan Jul 24 '18 at 07:48
  • @dMilan, From your updates it's unclear you want to change your architecture, but it unclear what exactly you want. Seeing as it a new question, Please Remove your update, mark this as resolved. Then create a new clear question, link us to it. – johnny 5 Jul 24 '18 at 12:39
  • @dMilan thanks, I agree you should move the loading to the parent, it will avoid taking in a second generic parameter, send me a link to the next question and I’ll gladly answer ;) – johnny 5 Jul 24 '18 at 13:02

2 Answers2

1

It's very unclear what you want from the question, but from the looks of it you need multiple enumerable types, depending on what class you are using.

public abstract class Test<T, TEnumerable> : IRepositoryLoader<T>
    Where TEnumerable : IEnumerable<T>
{
    Func<IEnumerable<T>, TEnumerable> _enumerableResolver;
    public Test(Func<IEnumerable<T>, TEnumerable> enumerableResolver)
    {
         this._enumerableResolver = enumerableResolver
    }

    protected TEnumerable Source;
    public abstract IEnumerable<T> LoadData();

    private void IRepositoryRelouder.Reload() => 
        Source = this._enumerableResolver(LoadData());
}

Then you could call instanciation like so:

public class TestA: Test<TestItem, ConcurentBag<TestItem>>
{
   public TestA() : base(x => new ConcurentBag<TestItem>(x))
   {  
   }
}

Then again I don't even see why you need the second parameter in the first place: When you could just make your reload method abstract

protected abstract void Reload();

And then just have your class implement it.

public class TestA: Test<TestItem>
{
    protected void Reload() => new ConcurentBag<TestItem>(LoadData())
}
johnny 5
  • 19,893
  • 50
  • 121
  • 195
1

It's possible, but a little bit of a brittle design, since it relies on the fact that both ConcurrentBag<T> and List<T> can take an IEnumerable as the constructor argument and populate itself. If you need to support types where the constructor arguments are not consistent like this, I'd recommend johnny's answer.

Anyway, you just need a type constraint like this:

public abstract class Test<TItem,TContainer> : IRepositoryLoader<TItem> 
    where TContainer : class, IEnumerable<TItem>

And ReloadData() would look like this:

void IRepositoryReloader.ReloadData()
{       
    this.Source = (TContainer)Activator.CreateInstance(typeof(TContainer), new [] { LoadData() } );
}

See my full working example on DotNetFiddle

John Wu
  • 50,556
  • 8
  • 44
  • 80