Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow derived type cast as parent type to return `object`, but using the derived type's method

Given the following code (which I know won't compile and isn't correct):

public abstract class Fetcher {
   public abstract IEnumerable<object> Fetch();
}

public class Fetcher<T> : Fetcher {
   private readonly Func<IEnumerable<T>> _fetcher;
   public Fetcher(Func<IEnumerable<T>> fetcher) { _fetcher = fetcher; }
   public IEnumerable<T> Fetch() => _fetcher(); // override or new
}

And this example setup:

var myFetchers = new List<Fetcher> {
   new Fetcher<string>(() => new List<string> { "white", "black" })),
   new Fetcher<int>(() => new List<int> { 1, 2 }))
};

How can I structure my code so that this will work?

IEnumerable<IEnumerable<object>> fetcherResults =
   myFetchers.Select(fetcher => fetcher.Fetch()); // get objects from derived method

TLDR; I've thought about this a little more. A different way to state my question is: how do I run the Fetch method on all the items in the myFetchers list and save a collection of their results, without having to determine the base type of each item and do a cast to that type, while not returning IEnumerable<object> from the Fetch method itself?

The key here is that at the time the objects of different generic type are in a collection of the parent type, I want to run the derived type's method, but return the result as IEnumerable<object>, not IEnumerable<T>. (This can be done using a different method if necessary, I suppose, though it would be nice if it were the same name.) This is so that a section of the code that doesn't need to know about fetchers can have its data (and just be passed fetcherResults), but the part of the code that does care about fetchers can completely ignore what type of data the fetchers work with (not its concern, only the consumer of the data needs to work with the specific types).

I don't have to use inheritance here, and interface IFetcher would work just as well. I keep thinking about how explicit interface implementation might help, but I'm just not closing the loop right now how to make this work for me:

// in a class implementing ICollection<T>
public IEnumerator<T> GetEnumerator() => _collection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) _collection).GetEnumerator();

As usual, the real situation is more complicated than this (such as the data the fetchers are providing coming from a database, etc.). But getting this one part down will be all I need.

I have looked at the following resources to no avail: implement an inheritable method that returns an object, call method on generic type from abstract parent.

like image 938
ErikE Avatar asked Jun 27 '16 19:06

ErikE


1 Answers

There is a straight-forward way to do this with explicit implementation as you suspected. This is only available if you use an interface rather than an abstract class.

public interface IFetcher {
    IEnumerable<object> Fetch();
}

public class Fetcher<T> : IFetcher  {
    private readonly Func<IEnumerable<T>> _fetcher;
    public Fetcher(Func<IEnumerable<T>> fetcher) { _fetcher = fetcher; }
    IEnumerable<object> IFetcher.Fetch() => Fetch().Cast<object>();
    public IEnumerable<T> Fetch() => _fetcher();
}

In this implementation, the interface version of the call returns IEnumerable<object>, while the class-accessible version returns the more specific IEnumerable<T>. If you call the fetcher through the interface, it will resolve to the first. Calling through the class will resolve to the second.

like image 59
recursive Avatar answered Sep 30 '22 16:09

recursive