通过放下继承树在C#中输入"更宽松"

时间:2020-03-05 18:58:33  来源:igfitidea点击:

我想问的问题是:

是从抽象类内部抛弃继承树(即朝向更专业的类),还是可以原谅,甚至是一件好事,还是选择更好的选择总是一个糟糕的选择?

现在,举例说明为什么我认为它可以被很好地使用。

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

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

还有四个派生类,分别是BString,BInteger,BList和BDictionary,它们表示要编码的四种不同数据类型。现在,这是棘手的部分。 " BList"和" BDictionary"分别具有" 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文件中。泛型不是解决之道,这个问题至少:(。单击我链接的规范,它只有4个小点。

解决方案

回答

我们实际上不应该访问任何从基类派生的类,因为它几乎破坏了OOP的概念。可读性当然有很长的路要走,但我不会为了可重用性而将其交易。考虑这种情况,当我们需要添加另一个子类时,还需要相应地更新基类。

回答

如果我们经常检索文件长度,为什么不在BDictionary(?)类中实现属性...,这样代码将变为:

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

这样,实现细节就向用户隐藏了。

回答

我想我可以使this [int]和this [string]访问器虚拟化,并在BList / BDictionary中覆盖它们。访问器没有意义的类应强制转换为NotSupportedException()(也许通过在BItem中具有默认实现)。

这使代码以相同的方式工作,并在我们应该编写的情况下为我们提供了更具可读性的错误

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

因为失误。

回答

我们是否考虑过解析一个简单的"路径",所以可以这样写:

也许不是最好的方法,但是可读性增加了一点

回答

  • 如果我们完全控制自己的代码库和思想过程,则一定可以。
  • 如果不是这样,我们会后悔这一天,有些新人注入了BItem派生类,而我们没有看到它进入BList或者BDictionary。

如果必须执行此操作,请至少将其包装(控制对列表的访问)到具有强类型方法签名的类中。

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

接受并返回BString,即使我们将其内部存储在BItems的BList中也是如此。 (让我分裂,然后再达到2 B或者不达到2 B)

回答

唔。我实际上会辩称,编码的第一行比第二行更具可读性,因此弄清楚正在发生的事情要花费更长的时间,但是更明显的是我们将对象视为BList或者BDictionary。将方法应用于抽象类将隐藏该细节,这可能使弄清楚方法实际在做什么变得更加困难。

回答

我的看法是,并非所有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成为通用类。

回答

如果引入泛型,则可以避免强制转换。

class DecodedTorrent : BDictionary<BDictionary<BList<BDictionary<BInteger>>>>
{
}
DecodedTorrent torrent = BItem.DecodeFile("mytorrent");
int x = torrent["info"]["files"][0]["length"];

嗯,但这可能行不通,因为类型可能取决于我们通过结构的路径。

回答

只有我吗

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]{    get { return ((BList)this)[index]; }}public BItem this[string index]{    get { return ((BDictionary)this)[index]; }}

由于返回类型仍然是抽象版本,因此无法达到预期的结果,因此我们仍然必须进行强制转换。

重写的代码必须是

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

和第一批一样糟糕

回答

我的建议是引入更多抽象。我发现BItem有一个返回BDictionary的DecodeFile()令人困惑。我不知道在torrent域中这样做是合理的。

但是,我会发现以下类似的api更加合理:

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