9

次のようなクラスがあるとしましょう:

public sealed class Foo
{
    public void Bar
    {
       // Do Bar Stuff
    }
}

そして、拡張メソッドができることを超えて何かを追加するためにそれを拡張したい....私の唯一のオプションは構成です:

public class SuperFoo
{
    private Foo _internalFoo;

    public SuperFoo()
    {
        _internalFoo = new Foo();        
    }

    public void Bar()
    {
        _internalFoo.Bar();
    }

    public void Baz()
    {
        // Do Baz Stuff
    }
}

これは機能しますが、大変な作業です...しかし、まだ問題に遭遇します:

  public void AcceptsAFoo(Foo a)

ここで Foo を渡すことはできますが、スーパー Foo を渡すことはできません。なぜなら、C# は SuperFoo が Liskov Substitution の意味で本当に資格があるとは考えていないためです...これは、構成による私の拡張クラスの使用が非常に限られていることを意味します。

したがって、これを修正する唯一の方法は、元の API 設計者がインターフェースを残しておいてくれることを願うことです。

public interface IFoo
{
     public Bar();
}

public sealed class Foo : IFoo
{
     // etc
}

これで、SuperFoo に IFoo を実装できます (SuperFoo は既に Foo を実装しているため、署名を変更するだけです)。

public class SuperFoo : IFoo

そして完璧な世界では、Foo を消費するメソッドは IFoo を消費します。

public void AcceptsAFoo(IFoo a)

これで、C# は、共通のインターフェイスによって SuperFoo と Foo の関係を理解し​​、すべてがうまくいきました。

大きな問題は、.NET が、拡張すると便利な場合がある多くのクラスを封印し、通常は共通のインターフェイスを実装しないため、Foo を受け取る API メソッドが SuperFoo を受け入れず、オーバーロードを追加できないことです。 .

それで、そこにいるすべての作曲ファンのために....この制限をどのように回避しますか?

私が考えることができる唯一のことは、内部の Foo を公開して、時々渡すことができるようにすることですが、それは面倒です。

4

2 に答える 2

14

自分の再利用可能なライブラリに取り組み始めるまで、同じ質問をしていることに気づきました。多くの場合、実装者からのあいまいな、または不可解な一連の呼び出しを必要とせずに拡張できない特定のクラスに行き着きます。

クラスの拡張を許可する場合は、次の質問をする必要があります。開発者が私のクラスを拡張し、この新しいクラスをライブラリに渡す場合、この新しいクラスを透過的に操作できますか?この新しいクラスで適切に作業できますか?この新しいクラスは本当に同じように動作しますか?

ほとんどの場合、.Net Frameworkの封印されたクラスには、あなたが気付いていない特定の内部要件があり、現在の実装ではサブクラスに安全に公開できないことがわかりました。

これはあなたの質問に正確に答えるわけではありませんが、なぜすべてのクラスが.Net Frameworkで継承可能ではないのか(そしてなぜいくつかのクラスの封印も楽しまなければならないのか)についての洞察を提供します。

于 2009-02-16T18:56:28.983 に答える
4

残念ながら、短い答えは、必要なことをしなければできないということです。つまり、代わりに構成されたインスタンス変数を渡します。

その型への暗黙的または明示的なキャストを許可することもできます (その実装は単に構成されたインスタンスを渡します) が、これは IMO はかなり悪いことです。

sixlettervariable の回答は良いものであり、再ハッシュすることはしませんが、どのクラスを拡張したいかを示した場合、それらがそれを妨げた理由を教えてくれるかもしれません。

于 2009-02-16T19:19:05.793 に答える