允许派生类型转换为父类型返回' object ',但使用派生类型's方法

本文关键字:派生 类型 object 方法 返回 许派生 类型转换 父类 | 更新日期: 2023-09-27 17:52:52

给出以下代码(我知道这不会编译并且是不正确的):

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;我再考虑了一下。陈述我的问题的另一种方式是:我如何在myFetchers列表中的所有项目上运行Fetch方法并保存其结果的集合,而不必确定每个项目的基本类型并将其转换为该类型,同时不从Fetch方法本身返回IEnumerable<object> ?

这里的关键是,当不同泛型类型的对象在父类型的集合中时,我想运行派生类型的方法,但返回结果为IEnumerable<object>,而不是IEnumerable<T>。(我想,如果有必要的话,可以使用不同的方法来完成,不过如果使用相同的名称就更好了。)这样,一段不需要知道抓取器的代码就可以拥有它的数据(只需要传递fetcherResults),但是关心抓取器的那部分代码可以完全忽略抓取器处理的数据类型(不是它的关注点,只有数据的消费者需要处理特定类型)。

我不需要在这里使用继承,接口IFetcher也可以工作得很好。我一直在想显式接口实现可能会有什么帮助,但我现在还没有结束这个循环,如何为我工作:

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

与往常一样,实际情况比这更复杂(例如,获取器提供的数据来自数据库等)。但是把这一部分写下来就足够了。

我看了下面的资源都没有用:实现一个可继承的方法,返回一个对象,从抽象父类调用泛型类型的方法。

允许派生类型转换为父类型返回' object ',但使用派生类型's方法

正如您所怀疑的那样,有一种直接的方法可以通过显式实现来做到这一点。这只有在使用接口而不是抽象类时才可用。

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

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