2016-06-27 2 views
6

Учитывая следующий код (который я не знаю, буду компилировать и не правильно):Allow производного типа брошен как родительский тип для возврата `object`, но с помощью метода производного типа в

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 
} 

И это пример установки:

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

Как я могу структурировать свой код, чтобы это сработало?

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

TLDR; Я подумал об этом немного больше. Другой способ сформулировать мой вопрос: каким образом я могу запустить метод Fetch во всех элементах списка myFetchers и сохранить коллекцию своих результатов без необходимости определять базовый тип каждого элемента и делать приведение к этому типу, не возвращая IEnumerable<object> из самого метода Fetch?

Ключ здесь в том, что в то время, когда объекты другого родового типа находятся в коллекции родительского типа, я хочу запустить метод производного типа, но возвращать результат как IEnumerable<object>, а не IEnumerable<T>. (Это может быть сделано с использованием другого метода, если это необходимо, я полагаю, хотя было бы хорошо, если бы это было одно и то же имя.) Это значит, что раздел кода, который не должен знать о сборщиках, может иметь свои данные (и просто нужно пройти fetcherResults), но часть кода, которая заботится о сборщиках, может полностью игнорировать, с какими типами данных работают личители (а не с их заботой, только потребитель данных должен работать с конкретными типами).

Мне не нужно использовать наследование здесь, и интерфейс IFetcher будет работать так же хорошо. Я продолжаю думать о том, как явная реализация интерфейса может помочь, но я просто не закрывая петлю прямо сейчас, как сделать эту работу для меня:

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

Как обычно, реальная ситуация гораздо сложнее, чем это (например, как данные, которые получатели получают из базы данных и т. д.). Но получить эту часть вниз будет все, что мне нужно.

Я рассмотрел следующие ресурсы безрезультатно: implement an inheritable method that returns an object, call method on generic type from abstract parent.

+0

Почему бы не иметь 'IEnumerable FetchGeneric()' и 'IEnumerable Fetch()', который просто вызывает 'FetchGeneric(). В ролях ()'? –

+0

Думаю, меня отбросили, если бы они были с тем же именем. Теперь кажется очевидным, что вы упомянули об этом. Но если бы я решил отложить выборку до тех пор, пока не использовал производный тип, было бы неплохо получить конкретный тип, используя одно и то же имя метода. – ErikE

+0

Я обновил свой вопрос, см. Новый абзац, начинающийся с TLDR; – ErikE

ответ

3

Существует прямой способ сделать это с явной реализацией, как вы подозревали. Это доступно, только если вы используете интерфейс, а не абстрактный класс.

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(); 
} 

В этом варианте осуществления, версия интерфейса вызова возвращает IEnumerable<object>, а класс-доступная версия возвращает более конкретные IEnumerable<T>. Если вы вызываете сборщика через интерфейс, он будет разрешен к первому. Вызов через класс будет разрешен ко второму.

+0

Да! Это то, что я искал. Почему я не мог это увидеть? Спасибо! – ErikE

2

Если я правильно понял вопрос правильно, то вы можете решить эту проблему, сделав Fetcher<T> реализовать общий интерфейс IFetcher<T> так:

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

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

А затем создать адаптер, который может конвертировать IFetcher<TFrom> к IFetcher<TTo>, как это:

public class Adaptor<TFrom, TTo> : IFetcher<TTo> 
{ 
    private readonly IFetcher<TFrom> _fetcher; 

    public Adaptor(IFetcher<TFrom> fetcher) 
    { 
     _fetcher = fetcher; 
    } 

    public IEnumerable<TTo> Fetch() 
    { 
     return _fetcher.Fetch().Cast<TTo>(); 
    } 
} 

Это позволяет, например, для преобразования из IFetcher<string> в IFetcher<object>.

С помощью этого метода расширения:

public static class ExtensionMethods 
{ 
    public static Converter<TFrom> Convert<TFrom>(this IFetcher<TFrom> fetcher) 
    { 
     return new Converter<TFrom>(fetcher); 
    } 

    public class Converter<TFrom> 
    { 
     private readonly IFetcher<TFrom> _fetcher; 

     public Converter(IFetcher<TFrom> fetcher) 
     { 
      _fetcher = fetcher; 
     } 

     public IFetcher<TTo> To<TTo>() 
     { 
      return new Adaptor<TFrom, TTo>(_fetcher); 
     } 
    } 
} 

Вы можете сделать что-то вроде этого легко:

static void Main(string[] args) 
{ 
    var myFetchers = new List<IFetcher<object>> 
    { 
     new Fetcher<string>(() => new List<string> { "white", "black" }).Convert().To<object>(), 
     new Fetcher<int>(() => new List<int> { 1, 2 }).Convert().To<object>() 
    }; 

    var results = myFetchers.Select(x => x.Fetch()); 
} 

Пожалуйста, обратите внимание, что вы можете обойтись без метода расширения, но это сделает вашу код более читабельный.

+0

Я понимаю. Вы говорите, чтобы позже обмануть Fetcher с другой косвенностью, которая берет базовый Fetcher, а затем реализует интерфейс, возвращающий объект, так что позже, когда мы 'Fetch', мы запускаем правильный метод, но он преобразуется в нужный тип объекта. Вы позволяете компилятору вывести тип, а затем сохранить это знание в обертке. Я должен подумать об этом, это полезно, но не уверен, что это маршрут, по которому я хочу спуститься. – ErikE

Смежные вопросы