2008-09-17 96 views
5

我想问的问题在C#“宽松”打字是这样的:铸造沿着继承树

从一个抽象类情有可原内铸造沿着继承树(即朝着更specialiased类)。甚至是一件好事,还是总是一个可怜的选择,有更好的选择?

现在,为什么我认为它可以用于良好的例子。

我最近在C#中实现了Bencoding from the BitTorrent protocol。一个足够简单的问题,如何表示数据。我选择这样做,

我们有一个abstract BItem类,它提供了一些基本功能,包括用于将Bencoded字符串解码为必要结构的static BItem Decode(string)

还有四个派生类,BStringBIntegerBListBDictionary,表示待编码的四个不同的数据类型。现在,这是棘手的部分。 BListBDictionary分别具有this[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],都将是在90%的torrent文件中。泛型是不是要走的路,与这个问题atleast :(。点击通过规范我链接,它只有4个小点的大点。

+0

我震惊了! :) – 2008-09-17 10:58:30

回答

5

我想我会使这[int]和这[string]访问器虚拟并在BList/BDictionary中覆盖它们。访问器没有意义的类应该施加NotSupportedException()(可能通过在BItem中具有默认实现)。

这使得以同样的方式你的代码的工作,让您在情况更可读的错误,你应该错误地写

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

+0

作为一个旁注,这是我实际上正在做的,除了我使用new来覆盖而不是虚拟/覆盖。愚蠢,愚蠢。更清洁,更好的解决方案,谢谢! – 2008-09-17 11:42:26

+1

'new'实际上并未覆盖。你只要得到两个同名的方法 - 如果有人投向基类,然后调用方法,他们将获得原始的基类方法,而不是覆盖的方法 – 2008-09-18 00:10:16

3

你真的不应该从基地访问任何派生类因为它很大程度上打破了OOP的概念,可读性肯定会有很大的提高,但是我不会为了可重用性而进行交易,考虑一下你需要添加另一个子类的情况 - 你还需要更新基类因此类

1

如果文件长度是你经常检索的东西,为什么不落实的BDictionary属性类......,让你的代码变得(?):

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

这样,用户就可以隐藏实现细节。

+0

如果你阅读规范(链接到,并且长6行),BDictionary可以在数据结构中的任何地方,所以这个答案根本没有意义,对不起。 – 2008-09-17 11:13:32

0

你有没有海外商品会有解析一个简单的“路径”,所以你可以这样写:

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

也许不是最好的方式,但可读性上升(少许)

0
  • 如果您完全控制了您的代码库和思考过程,那么务必采取一切行动。
  • 如果没有,您会后悔当天某些新人注入了您没有看到您的 BList或BDictionary的BItem派生。

如果必须这样做,atleast会在强类型方法签名的类中包装它(控制对列表的访问)。

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

即使您在内部将其存储在BItems的BList中,也会接受并返回BString。 (让我分开之前,我做我的2 B或不2 B)

0

嗯。我实际上会争辩说,编码的第一行比第二行更具可读性 - 要弄清楚发生了什么,花费更长的时间,但它更像是将对象视为BList或BDictionary。将这些方法应用到抽象类中隐藏了这些细节,这可能会让您很难弄清楚您的方法实际上在做什么。

1

我看到它的方式,并非所有的BItems都是集合,因此并非所有的BItems都有索引器,所以索引器不应该放在BItem中。我会从BItem派生另一个抽象类,我们将其命名为BCollection,并把索引存在,是这样的:

abstract class BCollection : BItem { 

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

,使BList和BDictionary从BCollection继承。 或者你可以多走一步,让BCollection成为一个通用类。

+0

访问者的返回BItem的,但这仍然没有帮助任何长期的申请。 – 2008-09-17 11:12:10

0

如果您引入泛型,您可以避免投射。

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

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

嗯,不过那可能是行不通的,因为这些类型也取决于你采取通过结构的道路上。

+0

EEK!好吧,这个(非常)具体的例子...但实际上,它是一个可变的树结构... `torrent [“info”] [“files”]`是有效的。但是`torrent [“announce-list”] [0] [0]`也是如此。你的泛型刚刚破解:( – 2008-09-17 11:34:45

0

难道只是我

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

你不需要BDictionary投“种子”被声明为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]; }} 

这些不acheive期望的结果返回类型仍然是抽象版本,所以你仍然需要施放。

重写的代码必须是

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

哪个是一样糟糕的第一批

+0

中间部分被添加到抽象类(BItem)中,因此他们有访问器,不需要再施放它们来使用它们。 – 2008-09-17 12:22:34

1

我的建议是推出更多的抽象。我发现令人困惑的是一个BItem有一个DecodeFile(),它返回一个BDictionary。这可能是一个合理的事情,在洪流领域做,我不知道。

不过,我会找到像以下较为合理的API:

BFile torrent = BFile.DecodeFile("my.torrent"); 
int filelength = torrent.Length;