繁体   English   中英

是否可以在 C# 中进行“递归参数多态性”

[英]Is it possible to do 'recursive parametric polymorphism' in C#

因此,我正在使用 .NET Core,并且正在制定一些需要可扩展的数据合约,为此,我打算使用参数多态性,即 generics。

现在,我的示例接口和类如下所示:

public interface IEmailAttachment<TEmail, TAttachment> where TEmail : IEmail<TAttachment> where TAttachment : IEmailAttachment<IEmail<TAttachment>, TAttachment>
{
    ...
    TEmail Email { get; set; }
    ...
}

public interface IEmail<TAttachment> where TAttachment : IEmailAttachment<IEmail<TAttachment>, TAttachment>
{
    ...
    IEnumerable<TAttachment> Attachments { get; set; }
    ...
}

public class DefaultEmail : IEmail<DefaultEmailAttachment>
{
    ...
    public IEnumerable<DefaultEmailAttachment> Attachments { get; set; }
    ...
}

public class DefaultEmailAttachment : IEmailAttachment<DefaultEmail, DefaultEmailAttachment>
{
    ...
    public DefaultEmail Email { get; set; }
    ...
}

但是,这不起作用。 我收到此错误:

对于 DefaultEmail class:

类型“DefaultEmailAttachment”不能用作泛型类型或方法“IEmail”中的类型参数“TAttachment”。 没有从“DefaultEmailAttachment”到“IEmailAttachment<IEmail, DefaultEmailAttachment>”的隐式引用转换。

对于 DefaultEmailAttachment:

类型“DefaultEmailAttachment”不能用作泛型类型或方法“IEmailAttachment<TEmail, TAttachment>”中的类型参数“TAttachment”。 没有从“DefaultEmailAttachment”到“IEmailAttachment<IEmail, DefaultEmailAttachment>”的隐式引用转换。

我意识到这可能是因为我假设有点多,并且可能会使用类型参数循环,但是如果我遗漏了一些明显可以使这项工作发挥作用的东西,请通知我,或者提出另一个关于如何工作的想法。

虽然这种合同一开始似乎是个好主意,但您不可避免地会遇到一连串(通常是无法解决的)问题。 此外,这种结构非常不灵活。 您将如何将不同类型的附件添加到 email 或不同类型的邮件到 email 列表?

您将如何为不同类型的附件动态创建电子邮件?

虽然在设计时很灵活,但 generics 不是动态的。 这意味着在编译时必须知道具体类型。

基本问题是:

  • 对于不同类型的附件,您真的需要不同类型的 email 吗?
  • 为什么附件需要知道 email 的确切型号?
  • 经典的多态性不是足够且更灵活吗?

中间也有办法。 通常,解决此类问题的一种方法是使用非泛型基接口(或类)和派生泛型类型:

public interface IAttachment
{
    IEmail Email { get; set; } // Only reference the non-generic base interface.
}

public interface IEmail
{
    // Only members not requiring a generic type parameter. 
}

public interface IEmail<TAttachment> where TAttachment : IAttachment
{
    ICollection<TAttachment> Attachments { get; set; }
}

现在您可以获得不同类型的 email 的列表

var mails = new List<IEmail>();
mails.Add(new EMail<SpecificAttatchment>());
mails.Add(new EMail<DefaultAttatchment>()); // or
mails.Add(new DefaultEmail());

这稍微灵活一些,但您仍然会遇到在运行时访问不同IEmail<T>类型的附件的问题。 我不知道你想用这种递归参数多态性解决什么类型的问题,但我会 go 单独使用非通用IAttachmentIEmail接口。 如果仍然需要泛型类型参数,请尽量避免递归。

Olivier 提出了一些好的观点。 我同意他的所有观点,并且不会特别推荐这种方法,因为理解这些限制是多么困难。 但是,是的,可以实施。 如果两个类具有相同的参数和相同的约束,则更容易理解:

public interface IEmailAttachment<TEmail, TAttachment> where TEmail : IEmail<TEmail, TAttachment> where TAttachment : IEmailAttachment<TEmail, TAttachment>
{
    TEmail Email { get; set; }
}

public interface IEmail<TEmail, TAttachment> where TEmail : IEmail<TEmail, TAttachment> where TAttachment : IEmailAttachment<TEmail, TAttachment>
{
    IEnumerable<TAttachment> Attachments { get; set; }
}

public class DefaultEmail : IEmail<DefaultEmail, DefaultEmailAttachment>
{
    public IEnumerable<DefaultEmailAttachment> Attachments { get; set; }
}

public class DefaultEmailAttachment : IEmailAttachment<DefaultEmail, DefaultEmailAttachment>
{
    public DefaultEmail Email { get; set; }
}

您当前看到的错误是因为Foo<Derived>默认情况下不可分配给Foo<Base> IEmailAttachment<DefaultEmail, DefaultEmailAttachment>IEmailAttachment<IEmail<DefaultEmailAttachment>, DefaultEmailAttachment> 这个问题可以用covariance解决,但它会限制你只使用 getter 而不是 get/set。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM