【发布时间】:2019-12-20 01:59:05
【问题描述】:
我正在尝试找到一种方法来对实现通用接口的类型集合进行分组。
这是我的测试设置
using System;
using System.Collections.Generic;
namespace Example
{
public interface ITypeA { }
public interface ITypeB { }
public class DescendantA1 : ITypeA { }
public class DescendantA2 : ITypeA { }
public class DescendantB1 : ITypeB { }
public class DescendantB2 : ITypeB { }
public interface ITypesMapper<in TTypeA, out TTypeB>
where TTypeA : ITypeA
where TTypeB : ITypeB
{
TTypeB Map(TTypeA typeA);
}
public class FirstMapper : ITypesMapper<DescendantA1, DescendantB1>
{
public DescendantB1 Map(DescendantA1 typeA) => throw new NotImplementedException();
}
public class SecondMapper : ITypesMapper<DescendantA2, DescendantB2>
{
public DescendantB2 Map(DescendantA2 typeA) => throw new NotImplementedException();
}
public class Program
{
static void Main(string[] args)
{
List<ITypesMapper<ITypeA, ITypeB>> types = new List<ITypesMapper<ITypeA, ITypeB>>();
types.Add(new FirstMapper());
types.Add(new SecondMapper());
}
}
}
上述示例的问题是我无法将FirstMapper 和SecondMapper 添加到集合中,因为我没有捕获它们的接口。 (C# 编译器说它不能将FirstMapper and SecondMapper 转换为ITypesMapper<TTypeA, TTypeB>
我认为通过将我的接口的通用参数限制为继承 ITypeA 和 ITypeB 接口的类型,并使它们成为协/逆变我可以实现这一点。
我可以解决此问题的一种方法是将我的 Mapper 类更改为继承 ITypesMapper<ITypeA, ITypeB>,但随后我会在其 Map() 方法中丢失强类型对象,我将不得不投掷。我不想要这个。
另一种方法是定义一个IMapper 接口,该接口将由ITypesMapper<,,> 继承,但是我无法定义Map() 方法来处理泛型参数。
我想知道是否有办法创建一组 Mapper 类(如 FirstMapper 和 SecondMapper),每个类都必须有一个可以使用泛型参数的 Map 方法并且有办法将所有这些统一在同一个界面下,然后我可以调用。
我可以更改设计中的所有内容,我只需要:
- 一个界面,我可以将它们全部放在后面(以便它们可以放在一个集合中)。
- 接口应该公开ITypeB Map(ITypeA input)方法
- 具体的映射器实现应该使用具体的类型(所以映射器应该有
DescendantB1 Map(DescendantA1 typeA)而不是ITypeB Map(ITypeA input)
【问题讨论】:
-
IEnumerable<String>可以转换为IEnumerable<Object>,因为从它出来的任何字符串都是一个对象。IList<String>不能转换为IList<Object>,因为任何 添加 的对象都可能不是字符串。IEnumerable<T>的类型参数是out参数。您的TTypeA参数是in,这会给您带来麻烦。如果您可以将其更改为out,那就太好了。但你不能。剩下的就是计划 B:一个非通用的“基本”接口ITypesMapper,类似于System.Collections.IList。 -
@Kobek 基接口可以为空,或者子接口可以显式实现。当然,您的通用 Map() 原型是整个练习的重点,放弃它几乎没有意义。但是
System.Collections.Generic.List<T>implementsSystem.Collections.IListexplicitly. -
@Kobek 显式实现它让你有一个 explicit 非泛型
ITypeB ITypesMapper.Map(ITypeA typeA);方法,它满足显式实现的非泛型接口 但不与 通用public TTypeB Map(ITypeA TypeA);。就像List<T>在链接的参考源中所做的那样:int System.Collections.IList.Add(Object item)和public int Add(T item)。但是我们仍然有一个问题,当您从集合中检索这些东西时,您将如何使用它们。 -
@Kobek 存储然后是微不足道的,如图所示。来说说使用吧。请提供有关您计划如何使用它们的更多详细信息。在我看来,在使用时,所有具体类型都必须在编译时知道。对吗?
-
所以你的映射器在编译时接受一个具体类型未知的
ITypeA,并在编译时给你一个具体类型未知的ITypeB?泛型可以是答案的一部分,但在这一点上,要么你必须能够将ITypeB转换为编译时已知的某个具体类型,要么你必须能够做你需要做的一切通过非泛型 OOP 来处理它:虚拟、接口等。泛型非常不适合像这样的动态东西。当然,在类的具体实现中随意使用它们。
标签: c# oop inheritance types interface