“更宽松” 通过向下转换继承树来输入 C#

发布于 2024-07-04 07:18:43 字数 1657 浏览 7 评论 0原文

因此,我想问的问题是:

从抽象类内部向下转换继承树(即朝向更专业的类)是否可以原谅,甚至是一件好事,或者它总是一个糟糕的选择,有更好的选择?

现在,我举一个例子来说明为什么我认为它可以永久使用。

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

我们有一个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 文件中。 泛型不是解决办法,至少有这个问题:(。点击我链接的规范,它只有 4 个小点。

The question I want to ask is thus:

Is casting down the inheritance tree (ie. towards a more specialiased class) from inside an abstract class excusable, or even a good thing, or is it always a poor choice with better options available?

Now, the example of why I think it can be used for good.

I recently implemented Bencoding from the BitTorrent protocol in C#. A simple enough problem, how to represent the data. I chose to do it this way,

We have an abstract BItem class, which provides some basic functionality, including the static BItem Decode(string) that is used to decode a Bencoded string into the necessary structure.

There are also four derived classes, BString, BInteger, BList and BDictionary, representing the four different data types that be encoded. Now, here is the tricky part. BList and BDictionary have this[int] and this[string] accessors respectively to allow access to the array-like qualities of these data types.

The potentially horrific part is coming now:

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

Well, you get the picture... Ouch, that's hard on the eyes, not to mention the brain. So, I introduced something extra into the abstract class:

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

Now we could rewrite that old code as:

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

Wow, hey presto, MUCH more readable code. But did I just sell part of my soul for implying knowledge of subclasses into the abstract class?

EDIT: In response to some of the answers coming in, you're completely off track for this particular question since the structure is variable, for instance my example of torrent["info"]["files"][0]["length"] is valid, but so is torrent["announce-list"][0][0], and both would be in 90% of torrent files out there. Generics isn't the way to go, with this problem atleast :(. Have a click through to the spec I linked, it's only 4 small dot-points large.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

小忆控 2024-07-11 07:18:43
  • 如果您可以完全控制您的代码库和思维过程,请务必这样做。
  • 如果没有,当某个新人将您没有看到的 BItem 派生注入您的 BList 或 BDictionary 时,您会后悔的。

如果必须这样做,至少将其包装在具有强类型方法签名的类中(控制对列表的访问)。

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

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

  • If you have complete control of your codebase and your thought-process, by all means do.
  • If not, you'll regret this the day some new person injects a BItem derivation that you didn't see coming into your BList or BDictionary.

If you have to do this, atleast wrap it (control access to the list) in a class which has strongly typed method signatures.

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

Accept and return BStrings even though you internally store it in a BList of BItems. (let me split before I make my 2 B or not 2 B)

五里雾 2024-07-11 07:18:43

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

这使得您的代码以相同的方式工作,并在您

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

错误编写时为您提供更具可读性的错误。

I think I would make the this[int] and this[string] accessors virtual and override them in BList/BDictionary. Classes where the accessors does not make sense should cast a NotSupportedException() (perhaps by having a default implementation in BItem).

That makes your code work in the same way and gives you a more readable error in case you should write

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

by mistake.

情仇皆在手 2024-07-11 07:18:43

您确实不应该从基类访问任何派生类,因为它几乎破坏了 OOP 的思想。 可读性当然有很长的路要走,但我不会用它来换取可重用性。 考虑当您需要添加另一个子类时的情况 - 您还需要相应地更新基类。

You really should not access any derived classes from the base class as it pretty much breaks the idea of OOP. Readibility certainly goes a long way, but I wouldn't trade it for reusability. Consider the case when you'll need to add another subclass - you'll also need to update the base class accordingly.

温折酒 2024-07-11 07:18:43

如果文件长度是您经常检索的内容,为什么不在 BDictionary (?) 类中实现一个属性...这样您的代码就变成:

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

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

If file length is something you retrieve often, why not implement a property in the BDictionary (?) class... so that you code becomes:

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

That way the implementation details are hidden from the user.

仅此而已 2024-07-11 07:18:43

在我看来,并非所有 BItem 都是集合,因此并非所有 BItem 都有索引器,因此索引器不应该位于 BItem 中。 我将从 BItem 派生另一个抽象类,我们将其命名为 BCollection,并将索引器放在那里,类似于:

abstract class BCollection : BItem {

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

并使 BList 和 BDictionary 继承自 BCollection。
或者您可以加倍努力,使 BCollection 成为一个泛型类。

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.

作业与我同在 2024-07-11 07:18:43

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

不过,我觉得像下面这样的 api 更合理:

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

My recommendation would be to introduce more abstractions. I find it confusing that a BItem has a DecodeFile() which returns a BDictionary. This may be a reasonable thing to do in the torrent domain, I don't know.

However, I would find an api like the following more reasonable:

BFile torrent = BFile.DecodeFile("my.torrent");
int filelength = torrent.Length;
在巴黎塔顶看东京樱花 2024-07-11 07:18:43

您是否考虑过解析一个简单的“路径”,以便可以这样写:

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

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

Did you concider parsing a simple "path" so you could write it this way:


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

Perhaps not the best way, but the readability increases(a little)

零時差 2024-07-11 07:18:43

难道只有我吗?

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"];

哪个和第一批一样糟糕

Is it just me

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

You don't need the BDictionary cast 'torrent' is declared as a BDictionary

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

These don't acheive the desired result as the return type is still the abstrat version, so you still have to cast.

The rewritten code would have to be

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

Which is the just as bad as the first lot

固执像三岁 2024-07-11 07:18:43

唔。 我实际上认为第一行编码比第二行更具可读性 - 需要更长的时间才能弄清楚它发生了什么,但更明显的是您将对象视为 BList 或 BDictionary。 将方法应用于抽象类会隐藏该细节,这会使您更难弄清楚您的方法实际上在做什么。

Hmm. I would actually argue that the first line of coded is more readable than the second - it takes a little longer to figure out what's going on it, but its more apparant that you're treating objects as BList or BDictionary. Applying the methods to the abstract class hides that detail, which can make it harder to figure out what your method is actually doing.

汹涌人海 2024-07-11 07:18:43

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

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

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

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

If you introduce generics, you can avoid casting.

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

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

Hmm, but that probably won't work, as the types may depend on the path you take through the structure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文