允许派生类型转换为父类型以返回`object`,但使用派生类型的方法

时间:2016-06-27 19:10:36

标签: c# generics inheritance

鉴于以下代码(我知道它不会编译并且不正确):

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>,而不是{{1 }}。 (如果需要的话,这可以使用不同的方法来完成,但是如果它是相同的名称会很好。)这样,不需要了解取件器的代码部分可以拥有其数据(并且只是传递IEnumerable<T>),但是关注提取器的代码部分可以完全忽略提取器使用的数据类型(不关心它,只有数据的使用者需要使用特定类型)。

我不必在这里使用继承,接口fetcherResults也可以正常工作。我一直在思考显式接口实现可能有多大帮助,但我现在还没有关闭循环如何使这个工作对我有用:

IFetcher

像往常一样,真实情况比这更复杂(例如提取者提供的数据来自数据库等)。但是把这一部分放下来将是我所需要的全部。

我查看了以下资源但无济于事:implement an inheritable method that returns an objectcall method on generic type from abstract parent

2 个答案:

答案 0 :(得分: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>。如果通过界面调用fetcher,它将解析为第一个。通过课程调用将解决到第二个。

答案 1 :(得分: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());
}

请注意,您可以不使用扩展方法,但它会使您的代码更具可读性。