C# 统一通用接口的实现

C# 统一通用接口的实现,c#,oop,inheritance,types,interface,C#,Oop,Inheritance,Types,Interface,我试图找到一种方法来对实现泛型接口的类型集合进行分组 以下是我的测试设置 using System; using System.Collections.Generic; namespace Example { public interface ITypeA { } public interface ITypeB { } public class DescendantA1 : ITypeA { } public class DescendantA2 : ITy

我试图找到一种方法来对实现泛型接口的类型集合进行分组

以下是我的测试设置

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和SecondMapper转换为ITypesMapper

我认为,通过将我的接口的泛型参数限制为继承ITypeA和ITypeB接口的类型,并使它们共同/相反,我可以实现这一点

解决这个问题的一种方法是将映射器类更改为继承ITypesMapper,但随后我会在它们的Map方法中释放强类型对象,并且必须强制转换。我不要这个

另一种方法是定义一个IMapper接口,该接口将由ITypesMapper继承,但是我无法定义Map方法来处理泛型参数

我想知道是否有一种方法可以创建一组映射器类,如FirstMapper和SecondMapper,其中每一个都必须有一个可以处理泛型参数的映射方法,并且有一种方法可以将它们统一到同一个接口下,然后我可以调用它们

我可以改变我设计中的一切,我只需要:

一个界面,我可以把他们都放在后面,以便他们可以在一个集合。 接口应公开ITypeB MapITypeA输入方法 具体的映射器实现应该与具体类型一起工作,因此映射器应该具有后代B1 MapDegenantA1 typeA而不是ITypeB MapTypeA输入
这是我认为您可以得到的最接近的解决方案,它还演示了您尝试执行的操作的问题-

public interface ITypeA { }
public interface ITypeB { }

public class DescendantA1 : ITypeA { }
public class DescendantA2 : ITypeA { }

public class DescendantB1 : ITypeB { }
public class DescendantB2 : ITypeB { }

public interface ITypeMapper<in TTypeA, out TTypeB>
{
    TTypeB Map(TTypeA typeA);
}

public abstract class TypesMapper<TTypeA, TTypeB>: ITypeMapper<ITypeA, ITypeB>
    where TTypeA : ITypeA
    where TTypeB : ITypeB 
{
    public ITypeB Map(ITypeA typeA) => internalMap(typeA);

    protected abstract TTypeB internalMap(TTypeA a);
}

public class FirstMapper : TypesMapper<DescendantA1, DescendantB1>
{
    protected override DescendantB1 internalMap(DescendantA1 typeA) => throw new NotImplementedException();
}

public class SecondMapper : TypesMapper<DescendantA2, DescendantB2>
{
    protected override DescendantB2 internalMap(DescendantA2 typeA) => throw new NotImplementedException();
}

public static class ProgramTest
{
    static void Main(string[] args)
    {
        List<ITypeMapper<ITypeA, ITypeB>> types = new List<ITypeMapper<ITypeA, ITypeB>>();
        types.Add(new FirstMapper());
        types.Add(new SecondMapper());
    }
}
告诉我们不能将它转换成混凝土

现在,我们可以向通用抽象类定义添加类约束,即

where TTypeA : class, ITypeA
然后抛出给出错误的那条线:

public ITypeB Map(ITypeA typeA) => internalMap(typeA as TTypeA);

但是没有什么可以阻止此列表的消费者调用Map。。。使用的参数不是TTypeA,这就是为什么整个方案从一开始就不是类型安全的。尽管如此,如果您正在使用某种检查,并且您真的只需要使用正确的具体类型调用Map函数,我认为这将使您达到您想要的目的

IEnumerable可以转换为IEnumerable,因为从中产生的任何字符串都是对象。无法将IList强制转换为IList,因为添加到它的任何对象都可能不是字符串。IEnumerable的类型参数是out参数。您的TTypeA参数处于中,这会给您带来麻烦。如果你能把它改成out,你就很好了。但你不能。这就剩下了计划B:一个非通用的基本接口ITypesMapper,类似于System.Collections.IList。@Kobek基本接口可以是空的,或者子接口可以显式实现它。当然,您的通用地图原型是整个练习的重点,放弃它几乎没有意义。但是System.Collections.Generic.List。@Kobek显式实现它可以让您拥有显式的非泛型ITypeB ITypesMapper.MapITypeA typeA;方法,该方法满足显式实现的非泛型接口,但与泛型公共TTypeB MapITypeA TypeA;不冲突;。就像列表在链接引用源中所做的那样:int System.Collections.IList.AddObject项和public int AddT项。但是我们仍然有一个问题,当您从集合中检索这些东西时,您将如何使用它们。@Kobek存储起来很简单,如图所示。让我们谈谈使用。请提供更多关于您计划如何使用它们的详细信息。在我看来,在使用时,必须在编译时知道所有具体类型。这是正确的吗?那么您的映射程序在编译时获取一个具体类型未知的ITypeA,并在编译时为您提供一个具体类型未知的ITypeB?泛型可能是答案的一部分,但在这一点上,要么你必须能够将ITypeB转换为编译时已知的某个具体类型,要么你必须能够通过非泛型OOP完成所有你需要做的事情:虚拟、接口等。泛型非常不适合这样的动态内容。当然,在类的具体实现中可以随意使用它们。谢谢你的回复。我继续做了一件类似的事情——使用@EdPlunkett建议的方法,显式地实现了基本映射器,然后将其调用为我的泛型类型。因此,与您提供的答案几乎相同。至于它不安全——是的,我知道这一点。我有一个机制可以保证我用正确的类型调用它。唯一可能发生的问题是将来——其他人很容易误用代码。
public ITypeB Map(ITypeA typeA) => internalMap(typeA as TTypeA);