Another C# critique

  • Thread starter christopher diggins
  • Start date
W

Willy Denoyette [MVP]

As I told you before C# 2.0 doesn't have .lib and static linking. Static
linking involves a native code compiler and a linker, these are the domain
of C++.

Willy.
 
C

christopher diggins

Jon Skeet said:
Two things spring to mind after only a very cursory glance

1) Garbage collection - you've got a sentence which doesn't finish

Pardon my density but which one?
2) User defined type values can't be used as constant expressions, but
you can certainly have readonly variables:

readonly MyValueType foo = new MyValueType(...);

What is it about my brain? I am having trouble expressing the whole
non-constness thing. What I mean to say is that I can't have a const
instance of a type. Even though the reference itself can be const.
 
C

christopher diggins

Andreas Huber said:
AFAICT, array<complex<float>> will be legal ("although constructed types may
be used as generics").

Regards,

Andreas

In C#, a generic type parameter cannot itself be a generic, although
constructed types may be used as generics. What is a complex<float> if it is
not a generic? Truthfully I don't have a clue what they mean by "constructed
type".
 
J

Jon Skeet [C# MVP]

christopher diggins said:
Pardon my density but which one?

Apologies - it was in value type vs reference type:

"There are more allocations on the heap than are strictly neccessary
leading to memory f"
What is it about my brain? I am having trouble expressing the whole
non-constness thing. What I mean to say is that I can't have a const
instance of a type. Even though the reference itself can be const.

In fact, a reference can't be const - only a variable can be const, and
the only type of reference variable that can be const is a string one.
Const is for constants, and constants aren't the same as immutable
objects.
 
A

Andreas Huber

christopher said:
In C#, a generic type parameter cannot itself be a generic, although
constructed types may be used as generics. What is a complex<float>
if it is not a generic? Truthfully I don't have a clue what they mean
by "constructed type".

complex< float > is a normal class/type. I guess with "constructed type"
they mean an instantiated generic what makes sense given the fact that they
are saying that C# will have nothing like C++ template template parameters
(note the double template).

Regards,

Andreas
 
R

Rob Teixeira [MVP]

I suppose it depends who you talk to and what you talk about :)
For example, I've had some communications with the Mono developers, and they
are outstanding people to deal with, and overall a great bunch of guys.
However, my personal experience with others in the community is far less
spectacular. If the word Microsoft is ever dropped near most of the people i
know from that camp, they revert from geek compatriot to
raving-zealot-burn-the-heretic mode. Frankly, i'm sick of it, and it
demonstrates only vast ignorance, childishness, and unprofessionalism. I
also believe a healthy number of them do get nowhere... just check the
various lists of projects. There are vast numbers of projects in open-source
purgatory that had a three year alpha, a brief beta, a huge log of bugs, and
abruptly *died*. The source is sitting there, rotting and half finished, in
hopes some other geek will revive it. Hell, this even happens with big
companies in open source. The UnitedLinux consortium flopped over and
officially died this week because its members couldn't get along (and MS
wasn't even one of them).

There's just too much pointless partisanship that has nothing to do with
technical merits or accuracy. The kind of stuff I have come to expect from
politicians and Larry Ellison, not engineers. Technology is science. Leave
the blind devotion and religious fervor for more important things.

And i'm not implying at all that these people don't also exist on the
non-OpenSource side of the fence. We have a healthy number in this NG too.

-Rob Teixeira [MVP]
 
C

christopher diggins

Sorry about the previous blank post, finger slipped. Damn trigger finger.

Jon Skeet said:
Apologies - it was in value type vs reference type:

"There are more allocations on the heap than are strictly neccessary
leading to memory f"

Thank you.
In fact, a reference can't be const - only a variable can be const, and
the only type of reference variable that can be const is a string one.

Quote from MSDN :

The const keyword is used to modify a declaration of a field or local
variable. It specifies that the value of the field or the local variable
cannot be modified
....
One of the types: byte, char, short, int, long, float, double, decimal,
bool, string, an enum type, or a reference type.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfconstpg.asp

This would seem to contradict you.
Const is for constants, and constants aren't the same as immutable
objects.

A const variable is an immutable variable though an immutable
variable is not neccessarily const. Do you not agree with me on that one?

Nonetheless, I still stand by my statement as in the critique.
 
R

Rob Teixeira [MVP]

GC:
Problem #2 : Unsafe contexts. There is a problem of being able to modify managed types.
This significantly undermines the effectiveness of a GC and renders all unsafe code as
potentially having classes of errors which are normally removed by a GC mechanism.
From the language specification :

There are certainly issues that can come up from misuse of unsafe code. The
example you cited from the MSDN talks about strings, for example. Strings
are immutable types in the CLI/CLR, which allows the CLR to do
optimizations. For example, assigning one string ref to another doesn't copy
the contents. Since the runtime knows strings shouldn't change, both
references point at the same character array until one is assigned to a
different value. If you use unsafe code with one ref, and change the value,
you are inadvertantly changing another string ref's data as well, which
could cause a series of bugs. However, this in no way affects the GC. The
objects themselves live on the managed heap, and the GC will eventually get
around to cleaning them. I *strongly urge* you to read up on the GC and how
it works internally. Having said that, let's look at a problem with our
string scenario that can't be solved without unsafe or unmanaged code -
let's say you create one string variable and decrypt cipher data into it
containing a password (or any secret bit of info - credit card number,
etc.). You do what you need to do in your function, and when the function
ends, your string variable goes out of scope. The string is now elegible for
collection. However, you have no idea how long it will remain in memory.
It's possible that it's there for some time, and let's say it gets paged
out. Now, a clever hacker can sniff through your page file for secret data,
and it will show up. Programmers who are wary of security concerns need to
wipe secrets out of memory as quickly as possible. Knowing that there is
only ever one reference to the string, there is no reason I can't pin that
string, and using unsafe or unmanaged code, zero it out. You may not like
unsafe code, and it does have the potential of burning developers who don't
know what they're doing, but it does have its legitimate uses. Remember that
you can turn off this feature by not allowing the compiler switch to be used
by your development team. C# will not compile unsafe code by default. You do
not mention that in your text.
Value Type / Reference Types
A type in C# declares its own passing and assigning semantics. This violates a
consistency rule of expected behaviour. Since objects are always reference types
this also leads to several small performance penalties :
a.. An extra word is always needed for the pointer
b.. There are more allocations on the heap than are strictly neccessary
leading to memory fragmentation
c.. There is more memory than strictly neccessary needed to be cleaned up
by the garbage collector
d.. Dereference of pointer penalty
As a result of having two kinds of types and in order to have some kind of unification
C# chose to use a boxing / unboxing semantic. Boxing and unboxing is both subtle and
confusing as it can lead to a surprising interpretation of straightforward
code.

Are you saying that there should only be either all value types or all
reference types? If so, which one would you use? Languages like Java use
exclusively reference types (except for special case primitives) because it
is the most flexible type. However, if you kept all reference types, then
you'd have nothing but types that fit your "perforance penalties". On the
other hand, value types have an enormous number of limitations compared to
reference types, so I can't even fathom a language with all value types.
What's your alternative?

Also, the managed heap doesn't suffer from memory fragmentation like heaps
from certain other languages do. Use of the GC allows all memory to be
allocated sequentially. As objects get collected, the space is compacted by
generation. However, let's pretend for a minute this isn't the case. How
does using a non-reference type change the way the heap is used? You are
still allocating and cleaning out memory, and you'd still be leaving
fragments of unallocated space. I don't quite understand this bullet point,
or the next one. Finally, if you are using a system that has no reference
types, how can you have references that point to the same data? Would you
have to copy all your data EVERY time? How would you synchronize instances
to maintain semantics of assignment? Wouldn't that suck for performance?
Special Primitives and Immutabilty

"a const field is a compile-time constant"
You still haven't answered my question about how you would allow reference
types (or other user-defined types) to have this behavior. This is where we
need to develop the syntax that allows programmers to specify literal values
for what could be relatively complex object graphs, THEN on top of that, the
compiler needs to have a mechanism to store this data in the program AND
reconstitute it at runtime. Then we run into the problem of read-only (which
C# can do) vs. immutability. If you reconstitute the value at runtime by
normal instantiation, you are only creating a read-only value (which C# can
already do). If you reconstitute the value in a truly const manner, you
effectively bypass constructors, which is disasterous. This is why all
languages i can think of don't allow this. If you are suggesting that C# is
doing something that is less than ideal, what is your ideal solution?
C# does not support delegating interface implementations to member classes.
This significantly restricts the ability to use interfaces extensively.

Example? I'm not entirely disagreeing with you, but I'm wondering if we are
exaggerating the problem by using "extensively"?
The reason I'm not disagreeing entirely is that i'm not exactly fond of the
syntax C# uses for implementing interfaces. However I disagree that C# can't
do this. It is perfectly capable of delegating interface method calls to
member classes... I think you mean delegating the method call to a method of
a *contained* class (rather than nested class, right?). Anyway, again, C# is
perfectly capable of doing this.
Missing Interface Extensions

Example please? Are you assuming that you can't define Interfaces in C#?
That's what this is reading like, and that certainly isn't true at all. C#
allows you to write classes, abstract classes, AND stand-alone interfaces.
Interfaces in C# have no implementation and are not tied to a class
definition, so I'm confused at what you mean in your statement. Class
inheritance is NOT the only means of polymorphism in C#. That is also an
incorrect statement. You can certainly implement interfaces and create
interfaces. Please clarify this point, because I don't think you understand
what C# is doing here.
Source File Layout
[The lack of seperate and explicit definitions, aka header files]
promotes an unstructured coding style and often leads to bugs that are not
immediately obvious without reading the automatically generated
documentation.

I'm not sure I agree with this. What type of bugs are we talking about here
and how do we know this happens often? If you make an assertion, you are
going to have to back it up or qualify it. You may *prefer* to have header
files for various reasons, but you shouldn't make a blanket statement about
bugs without having data to prove it, or at the very least giving a good
example (or more) of what types of bugs we're dealing with here. I also
think you are going to have to explain why header files are better in your
opinion than documentation... in a sense, they are both being viewed here as
documentation here, and both are nothing but text at this point.

-Rob Teixeira [MVP]
 
M

Matthew W. Jackson

With templates and generics, Complex<float> becomes a type of it's own, just
like Object or float. Therefore there's no reason why Array<Complex<float>>
wouldn't work. If you have an Array<T>, then T can certainly be passed
Complex<float>.

I think you're getting parameter and argument confused. The generic type
argument CAN be a generic type.

The generic type parameter cannot be a generic. For example, in C++
templates you can define a class such as this:

template < template < class A > class B >
class MyTemplateTemplateClass {
};

I've never used this, and many C++ compilers don't even support it.
Microsoft's didn't until very recently.

I've seen a few good uses of these in C++, but I can't honestly think of how
it would be useful in C# (at least with generics being implemented as they
are in C#).

And to do Array<Complex<float>> in C++ you don't need a template template
parameter.

--Matthew W. Jackson
 
J

Jon Skeet [C# MVP]

christopher diggins said:
Quote from MSDN :

The const keyword is used to modify a declaration of a field or local
variable. It specifies that the value of the field or the local variable
cannot be modified
...
One of the types: byte, char, short, int, long, float, double, decimal,
bool, string, an enum type, or a reference type.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/
vclrfconstpg.asp

This would seem to contradict you.

Slightly. Of course, you didn't keep quoting to:

<quote>
Therefore, the only possible values for constants of reference types
are string and null.
</quote>

So yes, although I was slightly wrong, I think in practice the only
type of reference variable that is *usefully* const is a string one, as
everything else would have to be null.
A const variable is an immutable variable though an immutable
variable is not neccessarily const. Do you not agree with me on that one?

Yes. const fields are much more restricted than just readonly variables
though. const
Nonetheless, I still stand by my statement as in the critique.

Unless I'm mistaken, the actual wording of your statement has now
changed anyway...
 
G

Guest

Why not? I heard that this was coming sometime, 2.1 then? We need the
ability to embed an assembly into anotiher assembly (.exe).
 
C

christopher diggins

Matthew W. Jackson said:
With templates and generics, Complex<float> becomes a type of it's own, just
like Object or float. Therefore there's no reason why
Array said:
wouldn't work. If you have an Array<T>, then T can certainly be passed
Complex<float>.

I think you're getting parameter and argument confused. The generic type
argument CAN be a generic type.

The generic type parameter cannot be a generic. For example, in C++
templates you can define a class such as this:

template < template < class A > class B >
class MyTemplateTemplateClass {
};

I've never used this, and many C++ compilers don't even support it.
Microsoft's didn't until very recently.

I've seen a few good uses of these in C++, but I can't honestly think of how
it would be useful in C# (at least with generics being implemented as they
are in C#).

And to do Array<Complex<float>> in C++ you don't need a template template
parameter.

--Matthew W. Jackson


How silly of me to confuse arguments and parameters! Thank you very much.
Now I understand.
 
C

christopher diggins

Rob Teixeira said:
There are certainly issues that can come up from misuse of unsafe code. The
example you cited from the MSDN talks about strings, for example. Strings
are immutable types in the CLI/CLR, which allows the CLR to do
optimizations. For example, assigning one string ref to another doesn't copy
the contents. Since the runtime knows strings shouldn't change, both
references point at the same character array until one is assigned to a
different value. If you use unsafe code with one ref, and change the value,
you are inadvertantly changing another string ref's data as well, which
could cause a series of bugs. However, this in no way affects the GC. The
objects themselves live on the managed heap, and the GC will eventually get
around to cleaning them. I *strongly urge* you to read up on the GC and how
it works internally. Having said that, let's look at a problem with our
string scenario that can't be solved without unsafe or unmanaged code -
let's say you create one string variable and decrypt cipher data into it
containing a password (or any secret bit of info - credit card number,
etc.). You do what you need to do in your function, and when the function
ends, your string variable goes out of scope. The string is now elegible for
collection. However, you have no idea how long it will remain in memory.
It's possible that it's there for some time, and let's say it gets paged
out. Now, a clever hacker can sniff through your page file for secret data,
and it will show up. Programmers who are wary of security concerns need to
wipe secrets out of memory as quickly as possible. Knowing that there is
only ever one reference to the string, there is no reason I can't pin that
string, and using unsafe or unmanaged code, zero it out. You may not like
unsafe code, and it does have the potential of burning developers who don't
know what they're doing, but it does have its legitimate uses. Remember that
you can turn off this feature by not allowing the compiler switch to be used
by your development team. C# will not compile unsafe code by default. You do
not mention that in your text.

I think I shouldn't confuse the compiler with the language spec. The
language spec clearly talks about unsafe contexts. What a compiler defaults
to is not a concern IMO. I agree on a whole that unsafe code has its uses.
And we both agree that it can burn developers who don't know what they are
doing (or who work with such people, etc.). The fact that a feature can be
unallowed (and of course not used) is true for almost any dangerous feature,
with the right compiler / pre-processor tool. But this does not negate my
critique of the feature in the first place. I also don't think I suggest
that modifying unmanaged code affects the GC. I am trying to indicate that
it undermines the role of the GC to provide a memory protection mechanism.
Are you saying that there should only be either all value types or all
reference types? If so, which one would you use? Languages like Java use
exclusively reference types (except for special case primitives) because it
is the most flexible type. However, if you kept all reference types, then
you'd have nothing but types that fit your "perforance penalties". On the
other hand, value types have an enormous number of limitations compared to
reference types, so I can't even fathom a language with all value types.
What's your alternative?

My alternative would that all types can be both value types and reference
types much like C++.
Also, the managed heap doesn't suffer from memory fragmentation like heaps
from certain other languages do. Use of the GC allows all memory to be
allocated sequentially. As objects get collected, the space is compacted by
generation. However, let's pretend for a minute this isn't the case. How
does using a non-reference type change the way the heap is used? You are
still allocating and cleaning out memory, and you'd still be leaving
fragments of unallocated space. I don't quite understand this bullet point,
or the next one.

Stack allocation is in a fixed location, the same memory is used over and
over again, with no gaps in between.
Finally, if you are using a system that has no reference
types, how can you have references that point to the same data? Would you
have to copy all your data EVERY time? How would you synchronize instances
to maintain semantics of assignment? Wouldn't that suck for performance?


"a const field is a compile-time constant"
You still haven't answered my question about how you would allow reference
types (or other user-defined types) to have this behavior. This is where we
need to develop the syntax that allows programmers to specify literal values
for what could be relatively complex object graphs, THEN on top of that, the
compiler needs to have a mechanism to store this data in the program AND
reconstitute it at runtime. Then we run into the problem of read-only (which
C# can do) vs. immutability. If you reconstitute the value at runtime by
normal instantiation, you are only creating a read-only value (which C# can
already do). If you reconstitute the value in a truly const manner, you
effectively bypass constructors, which is disasterous. This is why all
languages i can think of don't allow this. If you are suggesting that C# is
doing something that is less than ideal, what is your ideal solution?


Example? I'm not entirely disagreeing with you, but I'm wondering if we are
exaggerating the problem by using "extensively"?

If I wanted to "extensively use" interfaces I would be writing hundreds or
even thousands of implementation delegation functions.
The reason I'm not disagreeing entirely is that i'm not exactly fond of the
syntax C# uses for implementing interfaces. However I disagree that C# can't
do this. It is perfectly capable of delegating interface method calls to
member classes... I think you mean delegating the method call to a method of
a *contained* class (rather than nested class, right?). Anyway, again, C# is
perfectly capable of doing this.

Ah, yes. This will be important for me to differentiate.
Example please? Are you assuming that you can't define Interfaces in C#?
That's what this is reading like, and that certainly isn't true at all. C#
allows you to write classes, abstract classes, AND stand-alone interfaces.
Interfaces in C# have no implementation and are not tied to a class
definition, so I'm confused at what you mean in your statement. Class
inheritance is NOT the only means of polymorphism in C#. That is also an
incorrect statement. You can certainly implement interfaces and create
interfaces. Please clarify this point, because I don't think you understand
what C# is doing here.

I did not say nor intend to suggest that class inheritance is the only kind
of polymorphism. I am trying to point out a feature which would adds
significant expressiveness to interfaces but is missing. This feature only
exists in Heron as far as I know. I wrote a short paper about extensions at
http://www.heron-language.com/extensions.html. To summarize my viewpoint :
an extension allows an interface to perform even more like an abstract base
class. Adding it would allow code to use interfaces even more instead of
having to resort to abstract base classes in certain design scenarios. Is
this more clear? Any suggestions on how I can clear up things here?
Source File Layout
[The lack of seperate and explicit definitions, aka header files]
promotes an unstructured coding style and often leads to bugs that are not
immediately obvious without reading the automatically generated
documentation.

I'm not sure I agree with this. What type of bugs are we talking about here
and how do we know this happens often? If you make an assertion, you are
going to have to back it up or qualify it. You may *prefer* to have header
files for various reasons, but you shouldn't make a blanket statement about
bugs without having data to prove it, or at the very least giving a good
example (or more) of what types of bugs we're dealing with here. I also
think you are going to have to explain why header files are better in your
opinion than documentation... in a sense, they are both being viewed here as
documentation here, and both are nothing but text at this point.

-Rob Teixeira [MVP]

I just touched this bit up. Between you and me what I am trying to get at
are situiations like where because all of the member fields are not obvious
a programmer can accidentally add an extra field for the same purpose as one
that already exists because they forgot it was there, asnd the same thing
for functions. Forgetting where in the design they are, etc. Little silly
things like that that are easily recoverable.

Excellent points Rob! I have adjusted some of the points you mentioned to
try and make them more technically accurate and more clear. Maybe I will
have a critique that is actually worth something sometime soon.
 
S

Stu Smith

I'd just like to butt-in on question 12, public fields. One of my favourite
C++ questions (with no right or wrong answer) is: "given that we don't like
public data fields, and given that the people who implement STL libraries
are pretty smart, why is pair<A, B> generally implemented with two public
data fields?"
 
M

Matthew W. Jackson

No problem.

I too would have been pissed if C# wouldn't support Array<Complex<float>>.

Of course, I'm pretty sure that a generic Complex class in C# will require a
second type parameter in order to support numeric operations on the type,
but that's another matter. At first it bothered me, until I came across an
elegant solution involving a helper class to do the arithmetic. Then I came
across an even more elegant solution involving a Generic abstract class to
emulate template-specialization, but that's off topic for this discussion so
I'll leave it at that.

--Matthew W. Jackson
 
C

codymanix

Stu Smith said:
I'd just like to butt-in on question 12, public fields. One of my favourite
C++ questions (with no right or wrong answer) is: "given that we don't like
public data fields, and given that the people who implement STL libraries
are pretty smart, why is pair<A, B> generally implemented with two public
data fields?"

Uuuh...Performance? But wait! What was the Jit good for?
Yes, it can automatically inline calls to Properties. So what?
 
C

codymanix

- unsafe code

You have to place the unsafe-modifier in front of Methods that use unsafe
code
and compile with /unsafe that unsafe code is accepted, what else do you
want?
When using Interop some Functions called from native dll's returns their
results in form
of pointers so what would you do without pointers in c#?
- attributes

They can give the Compiler lots of Information about an Method, Optimization
hints,
and so on. Some Attributes are good for the Windows Forms Designer, an
Attribute
can tell it Wheather a Property should displayed in the Toolbox or not.
- garbage collection

You contradict yourself. What do you want? Unsafe code where you have to
free the
memory manually or Automatically? Thanks to GC memory allocation is much
faster
than in C/C++.
- non-deterministic destructors

Deterministic destructors are good for deallocating native resources only.
Theoretically
they could make dtors of value-types deterministic, but they don't. I don't
know why.
- Objects can't exist on the stack

You remember what happened in C++ when you declare an Object on the Stack,
store its Reference and use it elsewhere? Right, a GPF or undefined
behaviour.
- Type / Reference Types
- An extra word is always needed for the pointer

Why word? Why "extra". A Reference in C# comsumes exactly the same space as
a
Reference in C++ does. 4 Bytes.
- There are more allocations on the heap than are strictly neccessary
leading to memory fragmentation

Thanks to Mark&Sweep GC there is *no* memory fragmentation.
- Dereference of pointer penalty

Better than to always passing the whole object on the Stack.
- Boxing / Unboxing

What is better?

Console.WriteLine("{0} + {1} = {2}", a, b, c);

or

Console.WriteLine("{0} + {1} = {2}", new Integer(a), new Integer(b), new
Integer(c));
- Mutability

Huh? You can make very class or struct immutable when you declare no methods
that
can modify the state of the object. Thats what they did with System.String
for example.
- Classes as Modules and Programs

Maybe it could useful for allday use function like abs, sin, cos. But
misused leads to
programs that only have loose functions which aren't encapsulated in a
class.
At the end, you have prodedural programming is that what you want?
- Polymorphism through Class Inheritance
- Interface Delegation
- Interface Extensions
Huh?

- C# Missing Template Template Parameters

If that is really true, I'll hate them for that.
- Late Added Generics

Yes Indeed. The whole class-Library could have been done better with
Generics.
- Source File Layout

Isn't that what an automatically generated helpfile is good for? I hated it
in C++ where
I always when I wanted to modify a method's signature I had to do it in two
locations.
This was always very annoying.
- Public Fields

Sometimes you make Ad-Hoc code to see if it works the way you though. It
would
be annoying that I must declare every field in a class as a property. Our
app has some
classes with over 40 public field. Making them all a property which does
nothing than
returning and setting that variable would be very stupid, it gains nothing
but bloating code.
- Special Primitives

You can declare you own datatype in C# and make it feel real with operator
overloading.
For example you could declare a 24-Bit Integer and use it exactly as you
would be using an int.
- Is it a Property or is it a field?

You don't have to know that. Why? This is the implementor's concern. If
there is a
property "Color" you know that it can set or get the "Color". Does it matter
how it is
Implemented?
o.x = y;

can mean very different things depending on the following conditions :
Is x or y a property or variable?
[...]
Is o an object reference or a struct value?

The question is: Does that matter? You know what you have to know: You are
assigning y to o.x. All other things are implementation details that should
not be exposed.
- Microsoft

Yes. That is the biggest reason for me to hate C#. But for all other Reasons
I love C#.

One last question: What is your favourite programming language(s)?
 
C

codymanix

What is it about my brain? I am having trouble expressing the whole
non-constness thing. What I mean to say is that I can't have a const
instance of a type. Even though the reference itself can be const.

The whole const-stuff in C++ is stupid. Classes should be self-contained,
and therefore everybody should be able to modify them safely. This is
real-world,
or do you know objects in the real world that are constant? :)

Maybe in C++ it was neccesary due to the whole pointer-stuff and so on but
in other
languages it is not.
 
C

codymanix

*** NO MULTIPLE INHERITANCE ***!!!
Biggest fault of C# in my opinion.

If you can state only one example where multiple nheritance is required I'll
believe you,
otherwise not. Multiple Inheritance is just bad style used by poor
programmers.
All Multiple Inheritance scenarios are best solved using composition
instead.
 

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

Similar Threads

CSharp Coding Standards 18

Top