通用实现非通用接口模式有什么缺点吗?

Laz*_*zlo 5 c# generics design-patterns interface

我开始看到这种模式经常出现在我的代码中:

class Foo { }

interface IBar
{
    Foo Foo { get; }
}

class Bar<TFoo> : IBar where TFoo : Foo
{
    public TFoo Foo { get; private set; }

    Foo IBar.Foo
    {
        get 
        { 
            return Foo; 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它的一些好处是:

  1. 检查对象是否为包装类型 ( if (something is IBar))的简单方法
  2. TFoo封闭构造的Bar<>s 中的强类型访问
  3. Foo接口中IBar的多态访问

有人可能会争辩说,这种模式在框架中无处不在(例如List<T> : IList),但我想知道这是否只是 .NET 1.0 的残余,当泛型不存在时。

在我的脑海里,我主要担心的是,这IBar不一定是一个适当的合同,它定义了“酒吧”应该提供什么成员;访问通用类型的成员只是一种技巧。

此外,如果我开始为此目的添加接口,我很快就会难以维护并行继承层次结构

我应该担心在我的代码库中传播这种模式吗?如果是这样,哪些替代模式可以提供上面列出的部分或全部 3 个好处?

因为不允许显式实现抽象成员,所以这个“理想”的解决方案是不可能的:

class Foo { }

class Bar
{
    public abstract Foo Foo { get; }
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    private TFoo foo;

    Foo Bar.Foo
    {
        get
        {
            return foo;
        }
    }

    public new TFoo Foo
    {
        get
        {
            return foo;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Laz*_*zlo 0

回想起来,我了解到我想要的正确术语是返回类型协方差,不幸的是C# 不支持它,因为语言设计团队没有考虑实现该功能的好处超过成本,即使它保留了类型安全。(已经起草并完成了一项提案,但似乎已被放弃)。

使用返回类型协方差,示例代码可以写为:

class Foo { }

class Bar
{
    public virtual Foo Foo { get; }
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    public override TFoo Foo { get; }
}
Run Code Online (Sandbox Code Playgroud)

Eric Lippert 在该相关问题中提出的解决方法是:

class Foo { }

abstract class Bar
{
    protected abstract Foo foo { get; }
    public Foo Foo => foo;
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    protected override Foo foo => this.Foo;
    public new TFoo Foo { get { ... } }
}
Run Code Online (Sandbox Code Playgroud)

它的缺点是不复制继承层次结构,而是复制每个继承级别的每个协变模拟属性!

要进一步了解模拟协变返回类型会给代码带来多少混乱,请考虑正确实现意味着在每个继承级别ICloneable添加另一个虚拟方法。我将将此作为我对该语言功能的谦卑请求。