5

したがって、私が尋ねたい質問は次のとおりです。

抽象クラス内から継承ツリーを (つまり、より特殊化されたクラスに向かって) キャストすることは許されますか、それとも良いことでしょうか?

さて、それが良いことに使えると私が思う理由の例。

最近、C# でBitTorrent プロトコルから Bencoding を実装しました。データをどのように表現するかという単純な問題です。こうすることにしたのですが、

Bencoded 文字列を必要な構造にデコードするために使用されるなど、いくつかの基本的な機能を提供するabstract BItemクラスがあります。static BItem Decode(string)

、、、およびの4 つの派生クラスもありBString、エンコードされる 4 つの異なるデータ型を表します。さて、ここがトリッキーな部分です。また、これらのデータ型の配列のような性質にアクセスできるように、それぞれとアクセサーを持っています。BIntegerBListBDictionaryBListBDictionarythis[int]this[string]

潜在的に恐ろしい部分が今来ています:

BDictionary torrent = (BDictionary) BItem.DecodeFile("my.torrent");
int filelength = (BInteger)((BDictionary)((BList)((BDictionary)
             torrent["info"])["files"])[0])["length"];

さて、あなたは写真を手に入れます... 痛い、それは脳は言うまでもなく、目にも難しいです. そこで、抽象クラスに何か特別なものを導入しました。

public BItem this[int index]
{
    get { return ((BList)this)[index]; }
}
public BItem this[string index]
{
    get { return ((BDictionary)this)[index]; }
}

これで、古いコードを次のように書き直すことができます。

BDictionary torrent = (BDictionary)BItem.DecodeFile("my.torrent");
int filelength = (BInteger)torrent["info"]["files"][0]["length"];

うわー、さてさて、はるかに読みやすいコードです。しかし、サブクラスの知識を抽象クラスに暗示するために魂の一部を売っただけですか?

編集:いくつかの回答が寄せられたことに応じて、構造が可変であるため、この特定の質問については完全に軌道に乗っていませんtorrent["info"]["files"][0]["length"]torrent["announce-list"][0][0]そこに。ジェネリックは、少なくともこの問題では進むべき道ではありません:(。私がリンクした仕様をクリックしてください。それは4つの小さなドットポイントだけです。

4

10 に答える 10

5

this[int]およびthis[string]アクセサーを仮想化し、BList/BDictionaryでオーバーライドすると思います。アクセサが意味をなさないクラスは、NotSupportedException()をキャストする必要があります(おそらく、BItemにデフォルトの実装があるため)。

これにより、コードが同じように機能し、次のように記述した場合に備えて、より読みやすいエラーが発生します。

 (BInteger)torrent["info"][0]["files"]["length"];

間違って。

于 2008-09-17T11:02:16.053 に答える
3

基本クラスから派生したクラスにアクセスするべきではありません。OOPの概念をかなり壊してしまうからです。読みやすさは確かに大いに役立ちますが、私はそれを再利用性と交換するつもりはありません。別のサブクラスを追加する必要がある場合を考えてみましょう。それに応じて基本クラスも更新する必要があります。

于 2008-09-17T10:57:54.610 に答える
1

ファイルの長さが頻繁に取得するものである場合は、BDictionary(?)クラスにプロパティを実装してみませんか...コードは次のようになります。

BDictionary torrent = BItem.DecodeFile("my.torrent");
int filelength = torrent.FileLength;

これにより、実装の詳細がユーザーに表示されなくなります。

于 2008-09-17T11:02:02.620 に答える
1

The way I see it, not all BItems are collections, thus not all BItems have indexers, so the indexer shouldn't be in BItem. I would derive another abstract class from BItem, let's name it BCollection, and put the indexers there, something like:

abstract class BCollection : BItem {

      public BItem this[int index] {get;}
      public BItem this[string index] {get;}
}

and make BList and BDictionary inherit from BCollection. Or you could go the extra mile and make BCollection a generic class.

于 2008-09-17T11:07:33.527 に答える
1

私の推奨は、より多くの抽象化を導入することです。BItemにBDictionaryを返すDecodeFile()があるのは紛らわしいと思います。これは、トレントドメインで行うのが合理的なことかもしれませんが、私にはわかりません。

ただし、次のようなAPIの方が合理的です。

BFile torrent = BFile.DecodeFile("my.torrent");
int filelength = torrent.Length;
于 2008-09-17T18:56:48.407 に答える
0

単純な「パス」を解析して、次のように記述できるようにすることを検討しましたか。

BDictionary torrent = BItem.DecodeFile("my.torrent");
int filelength = (int)torrent.Fetch("info.files.0.length");

おそらく最善の方法ではありませんが、読みやすさが向上します(少し)

于 2008-09-17T11:04:05.097 に答える
0
  • コードベースと思考プロセスを完全に制御できる場合は、必ず実行してください。
  • そうでない場合は、この日、新しい人がBListまたはBDictionaryに表示されなかったBItem派生を注入したことを後悔します。

これを行う必要がある場合は、少なくとも、厳密に型指定されたメソッドシグネチャを持つクラスでラップします(リストへのアクセスを制御します)。

BString GetString(BInteger);
SetString(BInteger, BString);

BItemのBListに内部的に格納している場合でも、BStringを受け入れて返します。(2Bにする前に分割させてください。2Bにする前に分割させてください)

于 2008-09-17T11:06:43.607 に答える
0

うーん。私は実際、コード化された最初の行は2番目の行よりも読みやすいと主張します-何が起こっているのかを理解するのに少し時間がかかりますが、オブジェクトをBListまたはBDictionaryとして扱っている方がより明白です。メソッドを抽象クラスに適用すると、その詳細が非表示になり、メソッドが実際に何を行っているかを理解するのが難しくなる可能性があります。

于 2008-09-17T11:07:06.290 に答える
0

ジェネリックを導入すると、キャストを回避できます。

class DecodedTorrent : BDictionary<BDictionary<BList<BDictionary<BInteger>>>>
{
}

DecodedTorrent torrent = BItem.DecodeFile("mytorrent");
int x = torrent["info"]["files"][0]["length"];

うーん、しかし、型は構造体を通過するパスに依存する可能性があるため、おそらく機能しません。

于 2008-09-17T11:31:21.927 に答える
0

私だけでしょうか

BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BDictionary)((BList)((BDictionary)             torrent["info"])["files"])[0])["length"];

BDictionary キャスト 'torrent' が BDictionary として宣言される必要はありません

public BItem this[int index]{&nbsp; &nbsp; get { return ((BList)this)[index]; }}public BItem this[string index]{&nbsp; &nbsp; get { return ((BDictionary)this)[index]; }}

戻り値の型がまだ抽象バージョンであるため、これらは望ましい結果を達成しないため、キャストする必要があります。

書き換えられたコードは

BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BList)((BDictionary)torrent["info"]["files"])[0])["length"];

これは最初のロットと同じくらい悪いです

于 2008-09-17T12:10:33.203 に答える