0

Downcasts are a code smell. Implementing a base class; is there a way in C# to prevent the base class or interface from being downcasted by inheriting classes? The capability to derive from the base class should be preserved.

Example

interface IFoo { /* ... */ } // Only this interface can be modified.

class Bar : IFoo { /* ... */ }

void doSthWith(IFoo f)
{
    Bar b = (Bar) f; // to prohibit
}
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
ominug
  • 1,422
  • 2
  • 12
  • 28
  • In C++ you would explicitly delete the (automatically generated) function allowing this cast, or change its scope to private - can C# do something similar? – Conduit Sep 30 '14 at 17:34
  • I'll make this quick - No – user469104 Sep 30 '14 at 17:35
  • What is so negative about this question? – ominug Sep 30 '14 at 17:35
  • Maybe there is a annotation. – ominug Sep 30 '14 at 17:37
  • 2
    I don't think this can be prevented in C#. Though I'd also argue that it shouldn't be. The objects define their interfaces, it's not really their responsibility to prevent consuming code from mucking around with them. Downcasting is pretty low on the list of terrible things consuming code can do. (Reflecting into private members, for example.) If consuming code *wants* to break something, let it. And let it deal with the consequences. All the while the exposed interface still merrily exposes the supported functionality. – David Sep 30 '14 at 17:39
  • 3
    Maybe describe the actual problem that needs solving, as preventing casts surely isn't it. That's a mechanism, not a problem. – Anthony Pegram Sep 30 '14 at 17:39
  • There probably isn't a reasonable way to do this and I am not recommending this as a solution but you can create a kind of facade proxy that is not a "Bar", implements IFoo and passes through all of Bar's Methods and Properties to a private Bar. That would not be castable to Bar because it isn't a Bar. – Grax32 Sep 30 '14 at 17:39
  • @AnthonyPegram I think it is possible to answer to this question generally without a scenario. – ominug Sep 30 '14 at 18:00
  • 1
    Hey, your funeral. You're getting answers that apparently aren't satisfying. Meanwhile, your question reminds me of the [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), where one spends tremendous effort describing a solution that isn't working instead of the actual problem being faced. – Anthony Pegram Sep 30 '14 at 18:11
  • 2
    Let me add another perhaps unsatisfying way to prevent it: Not doing it. There, it's prevented. Another way: code reviews. Want to prevent someone else from doing it with their own derived classes? Why bother? Trust them to deal with their own code. – Anthony Pegram Sep 30 '14 at 18:13
  • 1
    Actually, SOLID design principals, which are widely known as a guide to best practices, **specifically** state in the liskov substitution principle that: "objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program." – C Bauer Sep 30 '14 at 18:24
  • @AnthonyPegram Always these well-meaning problem solvers. Thanks for your offer to help but I’m completely satisfied with correct answers. Else I would formulate my question differently. :) Stackoverflow is primarily a Q&A site, isn’t it? – ominug Oct 01 '14 at 20:06
  • @CBauer Downcasting is actually violating the Liskov substitution principle. Imagine another class `Bar2` which implements `IFoo`. Pass an instance of that class to the above method `doSthWith`. This substitutes an `IFoo` object by a `Bar2` object. Assuming further code in `doSthWith` -- it would potentially break. – ominug Oct 01 '14 at 20:25
  • 1
    @ominug Let me reword that: If you need to downcast your object, you're already doing something wrong, because you should be dealing with an object that is already replaceable with an instance of it's subtype without altering the correctness of your program. – C Bauer Oct 01 '14 at 20:44

4 Answers4

3

No

I won't argue with you on the code smell, but you can always attempt a downcast (it may of course, fail) from a base class to a derived class.

Even if it were possible, there are valid downcasting scenarios and if this is in a library, you would be restricting potential users.

See the casting article on MSDN for more info.

From the C# 5.0 Spec:

A cast-expression is used to explicitly convert an expression to a given type. cast-expression: ( type ) unary-expression A cast-expression of the form (T)E, where T is a type and E is a unary-expression, performs an explicit conversion (§6.2) of the value of E to type T. If no explicit conversion exists from E to T, a binding-time error occurs. Otherwise, the result is the value produced by the explicit conversion. The result is always classified as a value, even if E denotes a variable.

For an explicit reference conversion to succeed at run-time, the value of the source operand must be null, or the actual type of the object referenced by the source operand must be a type that can be converted to the destination type by an implicit reference conversion (§6.1.6) or boxing conversion (§6.1.7).

There is more, but nothing that would indicate a method to restrict casting.

Community
  • 1
  • 1
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • I face a scenario, where I think I should forbid it. – ominug Sep 30 '14 at 17:40
  • @ominug discussion of that aside, there isn't a way to restrict this, so you should find a different way of handling your scenario. – BradleyDotNET Sep 30 '14 at 17:41
  • @ominug you should explain the scenario in your question, maybe someone will know of a better way to handle it. – eddie_cat Sep 30 '14 at 17:42
  • I definitily saw somewhere that you can do this with Code Contracts. But I have no idea how – John Demetriou Apr 05 '17 at 13:23
  • Feature detection is one example of valid downcasting, though this usually involves down casting to other more specific interfaces from the generic interface. Hi `IFurniture`, do you implement `ISittable`? You do, then `ISittable.Sit(me)` – AaronLS Jun 15 '17 at 18:42
2

Downcasts are a code smell

I disagree with this statement, but let's ignore it for the moment.

You can't prevent a downcast from your base class, but you can make the derived classes internal (so that client code doesn't even know about them), and make the base class constructor internal as well to prevent client code from inheriting it (assuming, of course, that the code that will consume your class is in a different assembly).

The capability to derive from the base class should be preserved.

If you mean that you want client code to be able to inherit your class, then you won't be able to prevent a downcast.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
2

No. You can't prevent it, but you can make it less useful.

If you explicity implement the interface, then you can't call interface methods unless you have an interface reference.

public interface IFoo
{
    void Test();
}

public class Foo : IFoo
{
    void IFoo.Test() { }
}

Then code like below will not compile:

    IFoo x = new Foo();
    x.Test(); // works

    Foo y = (Foo)x;
    y.Test(); // will not compile
David Crowell
  • 3,711
  • 21
  • 28
  • OP stated that `Foo` was not editable (or `Bar` in original code). – Michael Sep 30 '14 at 19:25
  • I don't see that in the question, not even in edit history. What he wants is not in the C# language. This is possible way to work around it (to a degree). – David Crowell Sep 30 '14 at 19:32
  • 1
    It's implied in the question with the code comment _Only this interface can be modified_ (referring to `IFoo`) and in the response to Thomas' answer below. – Michael Sep 30 '14 at 19:52
2

As others have said, there's no way to prohibit the compiler from allowing such a downcast.

However if you're really motivated and use a compatible process, you could write a code analysis plugin for ReSharper of FxCop. These could be used to fail builds in a CI system.

I'm not sure I would personally be so draconian about something like this though.

EDIT I'm not 100% clear on how this might work, but you may also be able to use the Roslyn project in some way to achieve this. At the least, it'd make an interesting blog post for someone.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742