<p>The question I want to ask is thus:</p>

<p>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?</p>

<p>Now, the example of why I think it can be used for good.</p>

<p>I recently implemented <a href="http://www.bittorrent.org/beps/bep_0003.html#the-connectivity-is-as-follows" rel="nofollow noreferrer">Bencoding from the BitTorrent protocol</a> in C#. A simple enough problem, how to represent the data. I chose to do it this way,</p>

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

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

<p>The potentially horrific part is coming now:</p>

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

<p>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:</p>

<pre><code>public BItem this[int index]
{
    get { return ((BList)this)[index]; }
}
public BItem this[string index]
{
    get { return ((BDictionary)this)[index]; }
}
</code></pre>

<p>Now we could rewrite that old code as:</p>

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

<p>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?</p>

<p>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 <code>torrent["info"]["files"][0]["length"]</code> is valid, but so is <code>torrent["announce-list"][0][0]</code>, 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.</p>

[{'answer_id': 81990, 'author': 'petr k.', 'author_id': 15497, 'author_profile': 'https://Stackoverflow.com/users/15497', 'pm_score': 2, 'selected': False, 'text': "<p>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.</p>\n"}, {'answer_id': 82012, 'author': 'RickL', 'author_id': 7261, 'author_profile': 'https://Stackoverflow.com/users/7261', 'pm_score': 1, 'selected': False, 'text': '<p>If file length is something you retrieve often, why not implement a property in the BDictionary (?) class... so that you code becomes:</p>\n\n<pre><code>BDictionary torrent = BItem.DecodeFile("my.torrent");\nint filelength = torrent.FileLength;\n</code></pre>\n\n<p>That way the implementation details are hidden from the user.</p>\n'}, {'answer_id': 82013, 'author': 'Rasmus Faber', 'author_id': 5542, 'author_profile': 'https://Stackoverflow.com/users/5542', 'pm_score': 4, 'selected': True, 'text': '<p>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).</p>\n\n<p>That makes your code work in the same way and gives you a more readable error in case you should write</p>\n\n<pre><code> (BInteger)torrent["info"][0]["files"]["length"];\n</code></pre>\n\n<p>by mistake.</p>\n'}, {'answer_id': 82023, 'author': 'Stormenet', 'author_id': 2090, 'author_profile': 'https://Stackoverflow.com/users/2090', 'pm_score': 0, 'selected': False, 'text': '<p>Did you concider parsing a simple "path" so you could write it this way:</p>\n\n<p><p><code>\nBDictionary torrent = BItem.DecodeFile("my.torrent");<br />\nint filelength = (int)torrent.Fetch("info.files.0.length");\n</code></p><p>\nPerhaps not the best way, but the readability increases(a little)</p></p>\n'}, {'answer_id': 82037, 'author': 'Gishu', 'author_id': 1695, 'author_profile': 'https://Stackoverflow.com/users/1695', 'pm_score': 0, 'selected': False, 'text': "<ul>\n<li>If you have complete control of your codebase and your thought-process, by all means do.</li>\n<li>If not, you'll regret this the day some new person injects a BItem derivation that you didn't see coming into <em>your</em> BList or BDictionary.</li>\n</ul>\n\n<p>If you have to do this, atleast wrap it (control access to the list) in a class which has strongly typed method signatures. </p>\n\n<pre><code>BString GetString(BInteger);\nSetString(BInteger, BString);\n</code></pre>\n\n<p>Accept and return BStrings even though you internally store it in a BList of BItems. <em>(let me split before I make my 2 B or not 2 B)</em></p>\n"}, {'answer_id': 82040, 'author': 'John Christensen', 'author_id': 1194, 'author_profile': 'https://Stackoverflow.com/users/1194', 'pm_score': 0, 'selected': False, 'text': "<p>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.</p>\n"}, {'answer_id': 82044, 'author': 'neaorin', 'author_id': 15591, 'author_profile': 'https://Stackoverflow.com/users/15591', 'pm_score': 1, 'selected': False, 'text': "<p>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:</p>\n\n<pre><code>abstract class BCollection : BItem {\n\n      public BItem this[int index] {get;}\n      public BItem this[string index] {get;}\n}\n</code></pre>\n\n<p>and make BList and BDictionary inherit from BCollection.\nOr you could go the extra mile and make BCollection a generic class.</p>\n"}, {'answer_id': 82193, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 0, 'selected': False, 'text': '<p>If you introduce generics, you can avoid casting.</p>\n\n<pre><code>class DecodedTorrent : BDictionary&lt;BDictionary&lt;BList&lt;BDictionary&lt;BInteger&gt;&gt;&gt;&gt;\n{\n}\n</code></pre>\n\n<hr>\n\n<pre><code>DecodedTorrent torrent = BItem.DecodeFile("mytorrent");\nint x = torrent["info"]["files"][0]["length"];\n</code></pre>\n\n<p>Hmm, but that probably won\'t work, as the types may depend on the path you take through the structure.</p>\n'}, {'answer_id': 82399, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 0, 'selected': False, 'text': '<p>Is it just me</p>\n\n<pre><code>BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BDictionary)((BList)((BDictionary)             torrent["info"])["files"])[0])["length"];\n</code></pre>\n\n<p>You don\'t need the BDictionary cast \'torrent\' is declared as a BDictionary</p>\n\n<pre><code>public BItem this[int index]{&amp;nbsp; &amp;nbsp; get { return ((BList)this)[index]; }}public BItem this[string index]{&amp;nbsp; &amp;nbsp; get { return ((BDictionary)this)[index]; }}\n</code></pre>\n\n<p>These don\'t acheive the desired result as the return type is still the abstrat version, so you still have to cast.</p>\n\n<p>The rewritten code would have to be</p>\n\n<pre><code>BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BList)((BDictionary)torrent["info"]["files"])[0])["length"];\n</code></pre>\n\n<p>Which is the just as bad as the first lot</p>\n'}, {'answer_id': 86393, 'author': 'Thomas Eyde', 'author_id': 3282, 'author_profile': 'https://Stackoverflow.com/users/3282', 'pm_score': 1, 'selected': False, 'text': '<p>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.</p>\n\n<p>However, I would find an api like the following more reasonable:</p>\n\n<pre><code>BFile torrent = BFile.DecodeFile("my.torrent");\nint filelength = torrent.Length;\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/81972', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/15537/']