2

给定以下继承树,以有效的方式实现它的最佳方法是什么?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

这失败了:'Bar.Bar()' is inaccessible due to its protection level

我不希望构造函数是公开的,只有继承自的类Foo才能创建Bars。Bar是一个专门Foo的,任何类型的Foo都应该能够创建一个。公共内部在这里是一个“选项”,因为大多数预定义的扩展都Foo将在 DLL 内部,但我认为这是一个草率的答案,因为后来出现的任何人想要创建自己的Fooor类型Baz(即可能发生)将被默认CreateBar()实现卡住,这可能会也可能不会满足他们的需求。

也许有一种方法可以重构它以使其正常工作?我正在努力设计这个,所以它会起作用。

编辑(更多信息):

稍微具体一点:Foo 正在实现 IEnumerable,长话短说,Bar 提供了相同的接口,但只针对该可枚举对象的有限子集。所有 Foo 都应该能够创建自己的子集(即 Bar)并返回它。但我不想让每个想要实现 Foo 的人都担心这个,因为 Bar 会做代理并担心限制范围等。

4

5 回答 5

4

好的,新的答案:

  1. 将 Bar 拆分为一个接口和一个具体类。
  2. 用 IBar 表示公共抽象方法。
  3. 使 Bar 成为 Foo 中的私有嵌套类,实现 IBar。给它一个可以从 Foo 调用的内部构造函数。
  4. 在 Foo 中编写一个受保护的方法,该方法从自身创建 Bar 的一个实例。从 Foo 派生的类可以用它来实现抽象方法,只要代理就够了,需要更复杂的类可以直接实现 IBar。您甚至可以将抽象方法更改为虚拟方法,并默认从“this”创建一个新 Bar。

编辑:对此的一种变体是使 Bar 成为Foo 内的受保护嵌套类,并具有公共构造函数。这样,任何派生类都可以自己实例化它,但没有不相关的类能够“看到”它。您仍然需要将接口与实现分开(以便接口可以是公共的),但我认为无论如何这是一件好事。

于 2008-10-14T06:05:40.350 回答
1

您是否可以将 Baz 设为 Bar 中的嵌套类型?只有这样,您才能赋予它比其他方式更多的 Bar 访问权限。仅仅拥有相同的父类只能使其访问 Foo 的受保护成员,而 Foo 没有对 Bar 的特殊访问权限。我怀疑嵌套类型还有其他曲折的方法可以做到这一点,但实际上这对维护工程师来说会非常不愉快。

这是一个相当奇怪的设计,强制一个派生类创建从同一个基类派生的不同类的实例。这真的是你需要的吗?也许如果你把它放在更具体的术语中,那么提出替代设计会更容易。

于 2008-10-14T05:32:14.583 回答
1

您可以通过 Foo 中的嵌套类型访问 Bar 的构造函数:

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}
于 2008-10-14T05:56:16.373 回答
1

暂时忘记 Bar 源自 Foo。如果你这样做,听起来问题是“我如何才能让 Foo 的每个子类都可以创建一个 Bar,即使子类无法访问 Bar 的构造函数?”

这是一个很容易解决的问题:

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

如果要保证Foo 的子类无法访问 Bar 的构造函数,请将它们放在不同的程序集中。

现在,要从 Foo 派生 Bar,这是一个简单的更改:

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

“我不希望构造函数公开。” 查看。

“只有继承自 Foo 的类才能创建 Bars。” 查看。

“任何类型的 Foo 都应该能够创建一个。” 查看,

“后来出现的任何想要创建自己的 Foo 或 Baz 类型(这很可能发生)的人都将被默认的 CreateBar() 实现所困,这可能会或可能不会满足他们的需求。” 我认为这在很大程度上取决于 Foo.CreateBar() 方法中必须发生的事情。

于 2008-10-14T06:19:30.430 回答
0

C# 不提供 C++ 朋友关键字的直接等效项。似乎您的设计需要这种构造。

在 C++ 中,您可以使用“朋友”指定特定类可以访问另一个类的私有/受保护成员。注意:这与 C# internal, 修饰符不同,后者允许访问同一程序集中的所有类。

查看您的设计,您似乎正在尝试按照需要 C++ 风格朋友的方式做一些事情。Jon Skeet 是对的,C# 中通常的设计是使用嵌套类来弥补这一点。

论坛帖子进一步解释并展示了一些如何执行此操作的示例。

于 2008-10-14T05:49:40.460 回答