C# inheritance broken?

G

groups

C# is an impressive language...but it seems to have one big limitation
that, from a C++ background, seems unacceptable.

Here's the problem:

I have a third-party Document class. (This means I can't change the
Document class.) I want to extend this (inherit from Document) as
MyDocument, adding new events and application-specific methods and
properties.

I submit that this can't be done in C#.

Consider this example:

class MyDocument : public Document

This doesn't work because the only way to open an existing Document is
using the static method Document.Load(string FileName), which returns a
Document object. There doesn't seem to be any way to convert or cast a
Document object to a MyDocument object. There is therefore no way to
"Load" a MyDocument object!
From reading LOTS of posts on this group, the standard answers are:

1. "Wrap" the object, as follows:
class MyDocument
{
protected Document TheDocument;
}

But...this means that I have to wrap EACH of the hundreds of methods
and properties needed to manipulate the Document object. This is
clearly a case for inheritance, to allow the base class properties and
methods to be automatically available, with enhancements from the
derived class.

2. In MyDocument.Load, call Document.Load, and then COPY all of
Document's members to MyDocument.

BUT, this is prohibitive because COPYING all of Document's members is
prohibitive in terms of run time, and also in terms of development
effort (lines of code, and therefore, potential bugs).

In C++, I would do something like this:

class MyDocument : public Document
{
public static MyDocument Load(string FileName)
{
return (MyDocument)Document.Load(FileName);
}
}

So my conclusion is that C# inheritance is broken (in practice) because
of its strict type-checking, and no allowance for such a common
scenario.

Did I miss something?
 
A

Andy

How can something be broken when it functions as designed?

You also assume that a common scenario in C++ would be common in C#,
which is not a very good assumption either.

My answer would be create a static factory class which instantiates the
proper subclass. That is a pretty old pattern, predating C# or Java,
and you'd probably want to use it in C++ as well.
 
D

Daniel

Just a word of advice.

When posting on a newgroup of c#, don't come out with statements like 'c#
inheritence broken' just because you don't know the c# way of doing
something you are used to.

Better to say i can do this in c++ but can't seem to find a comparative
solution in c#, here is my problem etc etc. You'll find you get a much
better response.
 
G

groups

Andy,

Thanks for your response. My assertion is that the DESIGN is broken!
But I'll settle for a work-around, if there is one.

If I understand your suggestion, I've already tried this. But...I
don't have any problem creating a new MyDocument object when I need it;
my problem is loading a MyDocument from a file. There is only one
method in Document that does this:

class Document
{
public static Document Load(string FileName);
}

I can certainly write a new Load method to override the base method, as
follows:

class MyDocument : Document
{
public static MyDocument Load(string FileName)
{
return Document.Load(FileName);
}
}

The problem is, the above code won't compile, because it is not valid
to cast the Document returned by Document.Load to a MyDocument.

Does that make sense?

Tony
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

| C# is an impressive language...but it seems to have one big limitation
| that, from a C++ background, seems unacceptable.

Not a good start IMHO :)

| Here's the problem:
|
| I have a third-party Document class. (This means I can't change the
| Document class.) I want to extend this (inherit from Document) as
| MyDocument, adding new events and application-specific methods and
| properties.
|
| I submit that this can't be done in C#.

Why not?

Now, if the class is declared as sealed or the only constructor it has is a
private default constructor, well then maybe the designer did not want that
the class to be inheritable in the first place :)


| Consider this example:
|
| class MyDocument : public Document
|
| This doesn't work because the only way to open an existing Document is
| using the static method Document.Load(string FileName),

This is an indication that the class is not creatable, and the default
constructor is not public
Then we have two options:
1- it's declared protected:
protected void Document() {}
2- It's declared private
private void Document() {}


In the first case you can derive from Document without any problem, in the
later then or the designer was trying to avoid what you want to do or
simply made a mistake :)


| >From reading LOTS of posts on this group, the standard answers are:
|
| 1. "Wrap" the object, as follows:
| class MyDocument
| {
| protected Document TheDocument;
| }

Honestly I haven't worked in C++ in a while but I could bet that a similar
situation can be created in C++.


| In C++, I would do something like this:
|
| class MyDocument : public Document
| {
| public static MyDocument Load(string FileName)
| {
| return (MyDocument)Document.Load(FileName);
| }
| }

What if Document declare a private constructor? in this escenario there is
no way for MyDocyment to construct a Document and the compiler will complain
(the same escenario you have now in C# )

|
| So my conclusion is that C# inheritance is broken (in practice) because
| of its strict type-checking, and no allowance for such a common
| scenario.
|
| Did I miss something?

Yes, and I hope that my explanation is clear enough to see what you missed.
Post back if still have questions
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

| How can something be broken when it functions as designed?
|
| You also assume that a common scenario in C++ would be common in C#,
| which is not a very good assumption either.
|
| My answer would be create a static factory class which instantiates the
| proper subclass. That is a pretty old pattern, predating C# or Java,
| and you'd probably want to use it in C++ as well.

I thikn that in the OP case either the writter of Document wrote it as a
"sealed" class or simply made an error. The class is clearly not creatable
and apparentely is also not derivable ( sealed )

It has nothing to do with C# capabilities.
 
G

groups

Ignacio,

Thank you for your thoughts.

The Document class does have a public default constructor, and it is
not sealed. I have no problem creating a "new" Document object, or a
"new" MyDocument object, for that matter. What I can't do is "convert"
an existing Document object (such as the one returned by Document.Load)
into a MyDocument.

Tony
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

| Andy,
|
| Thanks for your response. My assertion is that the DESIGN is broken!

There is nothing wrong with teh design of C#.
What is broken is the design of the class you are trying to use :)

| But I'll settle for a work-around, if there is one.
|
| If I understand your suggestion, I've already tried this. But...I
| don't have any problem creating a new MyDocument object when I need it;
| my problem is loading a MyDocument from a file. There is only one
| method in Document that does this:
|
| class Document
| {
| public static Document Load(string FileName);
| }


Bad Design maybe?

If you do this you are certainly limiting yourself in that no derived class
will be able to override Load()

| I can certainly write a new Load method to override the base method, as
| follows:
|
| class MyDocument : Document
| {
| public static MyDocument Load(string FileName)
| {
| return Document.Load(FileName);
| }
| }
|
| The problem is, the above code won't compile, because it is not valid
| to cast the Document returned by Document.Load to a MyDocument.
|
| Does that make sense?


YES, and let me tell that again YES, IT DOES !

Not only that but it's the same IN ALL OTHER OOP LANGUAGES.


a MyDocument is a Document, the opposite (what you are trying to do) is not
true.
 
M

Mythran

Ignacio,

Thank you for your thoughts.

The Document class does have a public default constructor, and it is
not sealed. I have no problem creating a "new" Document object, or a
"new" MyDocument object, for that matter. What I can't do is "convert"
an existing Document object (such as the one returned by Document.Load)
into a MyDocument.

Tony
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,


|
|
| | > Ignacio,
| >
| > Thank you for your thoughts.
| >
| > The Document class does have a public default constructor, and it is
| > not sealed. I have no problem creating a "new" Document object, or a
| > "new" MyDocument object, for that matter. What I can't do is "convert"
| > an existing Document object (such as the one returned by Document.Load)
| > into a MyDocument.

As I said in another post, giving the struct you are presenting you would
have the same issue no matter what language you use. You cannot treat a base
class as a derived class, you can do the opposite though.


In your case you will have to implement your own Load method. I would first
check though if Document provide a Load method instance. or maybe a Clone
method
 
M

Michael D. Ober

Ignacio Machin ( .NET/ C# MVP ) said:
Hi,


|
|
| | > Ignacio,
| >
| > Thank you for your thoughts.
| >
| > The Document class does have a public default constructor, and it is
| > not sealed. I have no problem creating a "new" Document object, or a
| > "new" MyDocument object, for that matter. What I can't do is
"convert"
| > an existing Document object (such as the one returned by
Document.Load)
| > into a MyDocument.

As I said in another post, giving the struct you are presenting you would
have the same issue no matter what language you use. You cannot treat a
base
class as a derived class, you can do the opposite though.


In your case you will have to implement your own Load method. I would
first
check though if Document provide a Load method instance. or maybe a Clone
method
This isn't a C# issue. It is a dotNet issue as I have run into the same
issue of inheriting a base class (in my case, VB 2005 and StringCollection)
and providing a method for the base class object to become the object at the
heart of the derived class. This is a design limitation in the framework
itself. From a business logic perspective, it should be doable.

Mike Ober.
 
A

Andy

Thanks for your response. My assertion is that the DESIGN is broken!
But I'll settle for a work-around, if there is one.

The design can't be broken, by definition. The design dictates how a
piece of software should function, and C# functions as designed. Now,
you may disagree with the design decision, and that's another topic,
but to say its broken is wrong. Is a Toyota car's design broken
because it doesn't have as much horsepower as my car? No.
If I understand your suggestion, I've already tried this. But...I
don't have any problem creating a new MyDocument object when I need it;
my problem is loading a MyDocument from a file. There is only one
method in Document that does this:

class Document
{
public static Document Load(string FileName);

}I can certainly write a new Load method to override the base method, as
follows:

class MyDocument : Document
{
public static MyDocument Load(string FileName)
{
return Document.Load(FileName);
}

}The problem is, the above code won't compile, because it is not valid
to cast the Document returned by Document.Load to a MyDocument.

Does that make sense?

Not at all. You should be coding to abstracts. If you really need
MyDocument, then you'll instantiate MyDocument and not the base class.

If you want that code to work, its pretty simple:

public static MyDocument Load(string FileName)
{
MyDocument result;

result = new MyDocument();
DocumentLoader.Load( result, FileName );

return result;
}

The loading of your document is now abstracted away from the document
itself, which is a good thing.
 
A

Andy

Ahh, good point. In that case, I would direct the OP to read up on why
the sealed keyword is even needed.
 
G

groups

Ignacio,

There is no Load instance method in Document. Even if it had a Clone
method, this would cost too much time. As I pointed out before, C++
does indeed allow you to write such a "helper" class; you can use
reinterpret_cast to transform Document into a MyDocument.

Tony
 
G

groups

Andy,

In your example, where does

DocumentLoader.Load( result, FileName );

come from?

....and about the Toyota car, if they design one that requires you to
crawl underneath to start it, that design is broken. :)

Tony
 
B

Bruce Wood

C# is an impressive language...but it seems to have one big limitation
that, from a C++ background, seems unacceptable.

Here's the problem:

I have a third-party Document class. (This means I can't change the
Document class.) I want to extend this (inherit from Document) as
MyDocument, adding new events and application-specific methods and
properties.

I submit that this can't be done in C#.

<snip>

In C++, I would do something like this:

class MyDocument : public Document
{
public static MyDocument Load(string FileName)
{
return (MyDocument)Document.Load(FileName);
}

Now, I'll state first off that I'm not a C++ guru. I used it briefly.

That said, I'm having trouble imagining how this would work in any
language. Let me modify your example a bit:

class MyDocument : public Document
{
private int additionalFIeld = 15;

public static MyDocument Load(string FileName)
{
return (MyDocument)Document.Load(FileName);
}
}

So... you're telling me that C++ somehow, magically, knows that when
MyDocument.Load calls Document.Load it must allocate additional space
for the object for the fields that are a part of MyDocument but not
part of Document? How could it possibly know?

OR

Are you saying that in C++ you can cheat and cast a base type to a
derived type and get away with it so long as the derived type has no
additional fields? (And so, by inference, if the derived type _does_
have additional fields then the cast still works but all hell breaks
loose at runtime?)

If you're suggesting the latter, then from a .NET point of view it is
C++ that is "broken". .NET was explicitly designed to _not_ allow
programmers to play fast and loose with objects. The whole idea is to
_guarantee_ that casts are valid, and that pointers point to the right
thing, so as to mitigate all sorts of hacking attacks that depend upon
overlaying memory that contains one thing with a definition for a
different thing and then having a field day with arbitrary operations
in memory.

For example, the following is perfectly legal C:

char x[4];
int *p = (int *)&x;
int y = *p;

Memory is plastic in C/C++: you can flip between types and the compiler
assumes that you know what you're doing. In C#, the compiler assumes
nothing. The compiler / CLR combine to make solid guarantees about what
is what and what operations you're allowed to perform. As such, if you
want to stick to what's called "safe" code in C#, you can't play these
sorts of tricks. The compiler / runtime won't let you. There's nothing
broken about it: just different design goals.
 
A

Andy

Andy,

In your example, where does

DocumentLoader.Load( result, FileName );

come from?

You build it of course ;-)
...and about the Toyota car, if they design one that requires you to
crawl underneath to start it, that design is broken. :)

No, again, you disagree with the design, but its not broken, as long as
you can in fact start the car.
 
G

groups

Here is some more relevant information that I've just learned:

The Document.Load static method relies on System.Runtime.Serialization
to create a new Document object. Formatter.Deserialize returns an
object that is a Document, and this Document object can't be cast to a
MyDocument. Deserialization can't occur in a constructor, because the
Document object already exists, and you can't replace "this" with the
new Document object.

So it would seem that the limitation is there because of the way
serialization works, not because of a bad design on the part of the
author of Document.

Tony
 
M

Mark Wilden

As I pointed out before, C++
does indeed allow you to write such a "helper" class; you can use
reinterpret_cast to transform Document into a MyDocument.

Is this quite true? My understanding is that <reinterpret_cast> simply
performs an explicitly unsafe case of a pointer. It doesn't transform
anything. In this case, if a Document is created, it can never be
"transformed" into another type.

///ark
 
G

groups

You build it of course ;-)

Hmmm...I'm not sure how that would happen. I'm still stuck with the
fact that the third-party component's Load method returns a Document,
which can't be converted to type MyDocument, even in DocumentLoader.

Tony
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top